[MMIXER]
authorJohannes Anderwald <johannes.anderwald@reactos.org>
Sun, 20 Dec 2009 00:55:55 +0000 (00:55 +0000)
committerJohannes Anderwald <johannes.anderwald@reactos.org>
Sun, 20 Dec 2009 00:55:55 +0000 (00:55 +0000)
- Add support for enumerating wave in/out devices. Based on the wdmaud driver code

svn path=/trunk/; revision=44664

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/mmixer.rbuild
reactos/lib/drivers/sound/mmixer/priv.h
reactos/lib/drivers/sound/mmixer/wave.c [new file with mode: 0644]

index adc8187..b879da4 100644 (file)
@@ -827,6 +827,7 @@ MMixerInitializeFilter(
     ULONG BytesReturned;
     KSP_PIN Pin;
     LPWSTR Buffer = NULL;
+    ULONG PinId;
 
     // allocate a mixer info struct
     MixerInfo = (LPMIXER_INFO) MixerContext->Alloc(sizeof(MIXER_INFO));
@@ -864,10 +865,15 @@ MMixerInitializeFilter(
     // now get the target pins of the ADC / DAC node
     Status = MMixerGetTargetPins(MixerContext, NodeTypes, NodeConnections, NodeIndex, !bInputMixer, Pins, PinCount);
 
+    // find a target pin with a name
+    PinId = PinCount +1;
     for(Index = 0; Index < PinCount; Index++)
     {
         if (Pins[Index])
         {
+            // store index of pin
+            PinId = Index;
+
             /* retrieve pin name */
             Pin.PinId = Index;
             Pin.Reserved = 0;
@@ -900,6 +906,12 @@ MMixerInitializeFilter(
         }
     }
 
+    if (PinId < PinCount)
+    {
+        // create an wave info struct
+        MMixerInitializeWaveInfo(MixerContext, MixerList, MixerData, MixerInfo->MixCaps.szPname, bInputMixer, PinId);
+    }
+
     Status = MMixerCreateDestinationLine(MixerContext, MixerInfo, bInputMixer, Buffer);
 
     if (Buffer)
index d3abfb8..5e1ff8b 100644 (file)
@@ -251,3 +251,27 @@ MMixerSetGetControlDetails(
     DPRINT("Status %x bSet %u NodeId %u Value %d PropertyId %u\n", Status, bSet, NodeId, Value, PropertyId);
     return Status;
 }
+
+ULONG
+MMixerGetPinInstanceCount(
+    PMIXER_CONTEXT MixerContext,
+    HANDLE hFilter,
+    ULONG PinId)
+{
+    KSP_PIN PinRequest;
+    KSPIN_CINSTANCES PinInstances;
+    ULONG BytesReturned;
+    MIXER_STATUS Status;
+
+    /* query the instance count */
+    PinRequest.Reserved = 0;
+    PinRequest.PinId = PinId;
+    PinRequest.Property.Set = KSPROPSETID_Pin;
+    PinRequest.Property.Flags = KSPROPERTY_TYPE_GET;
+    PinRequest.Property.Id = KSPROPERTY_PIN_CINSTANCES;
+
+    Status = MixerContext->Control(hFilter, IOCTL_KS_PROPERTY, (PVOID)&PinRequest, sizeof(KSP_PIN), (PVOID)&PinInstances, sizeof(KSPIN_CINSTANCES), &BytesReturned);
+    ASSERT(Status == MM_STATUS_SUCCESS);
+    return PinInstances.CurrentCount;
+}
+
index 94c3a2a..d66116a 100644 (file)
@@ -215,7 +215,7 @@ MMixerGetLineControls(
     IN PMIXER_CONTEXT MixerContext,
     IN HANDLE MixerHandle,
     IN ULONG Flags,
-    OUT LPMIXERLINECONTROLS MixerLineControls)
+    OUT LPMIXERLINECONTROLSW MixerLineControls)
 {
     LPMIXER_INFO MixerInfo;
     LPMIXERLINE_EXT MixerLineSrc;
@@ -440,8 +440,13 @@ MMixerInitialize(
      //initialize mixer list
      MixerList->MixerListCount = 0;
      MixerList->MixerDataCount = 0;
+     MixerList->WaveInListCount = 0;
+     MixerList->WaveOutListCount = 0;
      InitializeListHead(&MixerList->MixerList);
      InitializeListHead(&MixerList->MixerData);
+     InitializeListHead(&MixerList->WaveInList);
+     InitializeListHead(&MixerList->WaveOutList);
+
 
      // store mixer list
      MixerContext->MixerContext = (PVOID)MixerList;
index bb272f3..1965613 100644 (file)
@@ -95,6 +95,15 @@ ULONG
 MMixerGetCount(
     IN PMIXER_CONTEXT MixerContext);
 
+ULONG
+MMixerGetWaveInCount(
+    IN PMIXER_CONTEXT MixerContext);
+
+ULONG
+MMixerGetWaveOutCount(
+    IN PMIXER_CONTEXT MixerContext);
+
+
 MIXER_STATUS
 MMixerGetCapabilities(
     IN PMIXER_CONTEXT MixerContext,
@@ -121,7 +130,7 @@ MMixerGetLineControls(
     IN PMIXER_CONTEXT MixerContext,
     IN HANDLE MixerHandle,
     IN ULONG Flags,
-    OUT LPMIXERLINECONTROLS MixerLineControls);
+    OUT LPMIXERLINECONTROLSW MixerLineControls);
 
 MIXER_STATUS
 MMixerSetControlDetails(
@@ -137,4 +146,24 @@ MMixerGetControlDetails(
     IN ULONG Flags,
     OUT LPMIXERCONTROLDETAILS MixerControlDetails);
 
+MIXER_STATUS
+MMixerWaveOutCapabilities(
+    IN PMIXER_CONTEXT MixerContext,
+    IN ULONG DeviceIndex,
+    OUT LPWAVEOUTCAPSW Caps);
+
+MIXER_STATUS
+MMixerWaveInCapabilities(
+    IN PMIXER_CONTEXT MixerContext,
+    IN ULONG DeviceIndex,
+    OUT LPWAVEINCAPSW Caps);
+
+MIXER_STATUS
+MMixerOpenWave(
+    IN PMIXER_CONTEXT MixerContext,
+    IN ULONG DeviceIndex,
+    IN ULONG bWaveIn,
+    IN LPWAVEFORMATEX WaveFormat,
+    OUT PHANDLE PinHandle);
+
 #endif
index 86644eb..582868a 100644 (file)
@@ -7,4 +7,5 @@
        <file>filter.c</file>
        <file>mixer.c</file>
        <file>sup.c</file>
+       <file>wave.c</file>
 </module>
index 0f91f44..0cdbd8c 100644 (file)
@@ -64,12 +64,28 @@ typedef struct
     LPWSTR DeviceName;
 }MIXER_DATA, *LPMIXER_DATA;
 
+typedef struct
+{
+    LIST_ENTRY Entry;
+    ULONG DeviceId;
+    ULONG PinId;
+    union
+    {
+        WAVEOUTCAPSW OutCaps;
+        WAVEINCAPSW  InCaps;
+    }u;
+}WAVE_INFO, *LPWAVE_INFO;
+
 typedef struct
 {
     ULONG MixerListCount;
     LIST_ENTRY MixerList;
     ULONG MixerDataCount;
     LIST_ENTRY MixerData;
+    ULONG WaveInListCount;
+    LIST_ENTRY WaveInList;
+    ULONG WaveOutListCount;
+    LIST_ENTRY WaveOutList;
 }MIXER_LIST, *PMIXER_LIST;
 
 #define DESTINATION_LINE 0xFFFF0000
@@ -243,4 +259,13 @@ MMixerGetDeviceName(
     IN LPMIXER_INFO MixerInfo,
     IN HANDLE hKey);
 
+MIXER_STATUS
+MMixerInitializeWaveInfo(
+    IN PMIXER_CONTEXT MixerContext,
+    IN PMIXER_LIST MixerList,
+    IN LPMIXER_DATA MixerData,
+    IN LPWSTR DeviceName,
+    IN ULONG bWaveIn,
+    IN ULONG PinId);
+
 #endif
diff --git a/reactos/lib/drivers/sound/mmixer/wave.c b/reactos/lib/drivers/sound/mmixer/wave.c
new file mode 100644 (file)
index 0000000..7e3ff34
--- /dev/null
@@ -0,0 +1,578 @@
+/*
+ * COPYRIGHT:       See COPYING in the top level directory
+ * PROJECT:         ReactOS Kernel Streaming
+ * FILE:            lib/drivers/sound/mmixer/mmixer.c
+ * PURPOSE:         Mixer Handling Functions
+ * PROGRAMMER:      Johannes Anderwald
+ */
+
+#include "priv.h"
+
+const GUID KSDATAFORMAT_SPECIFIER_WAVEFORMATEX  = {0x05589f81L, 0xc356, 0x11ce, {0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a}};
+const GUID KSDATAFORMAT_SUBTYPE_PCM             = {0x00000001L, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
+const GUID KSDATAFORMAT_TYPE_AUDIO              = {0x73647561L, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
+const GUID KSINTERFACESETID_Standard            = {0x1A8766A0L, 0x62CE, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
+const GUID KSMEDIUMSETID_Standard               = {0x4747B320L, 0x62CE, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
+
+typedef struct
+{
+    ULONG SampleRate;
+    ULONG Bit8Mono;
+    ULONG Bit8Stereo;
+    ULONG Bit16Mono;
+    ULONG Bit16Stereo;
+}AUDIO_RANGE;
+
+#define AUDIO_TEST_RANGE (5)
+
+static AUDIO_RANGE TestRange[AUDIO_TEST_RANGE] =
+{
+    {
+        11025,
+        WAVE_FORMAT_1M08,
+        WAVE_FORMAT_1S08,
+        WAVE_FORMAT_1M16,
+        WAVE_FORMAT_1S16
+    },
+    {
+        22050,
+        WAVE_FORMAT_2M08,
+        WAVE_FORMAT_2S08,
+        WAVE_FORMAT_2M16,
+        WAVE_FORMAT_2S16
+    },
+    {
+        44100,
+        WAVE_FORMAT_4M08,
+        WAVE_FORMAT_4S08,
+        WAVE_FORMAT_4M16,
+        WAVE_FORMAT_4S16
+    },
+    {
+        48000,
+        WAVE_FORMAT_48M08,
+        WAVE_FORMAT_48S08,
+        WAVE_FORMAT_48M16,
+        WAVE_FORMAT_48S16
+    },
+    {
+        96000,
+        WAVE_FORMAT_96M08,
+        WAVE_FORMAT_96S08,
+        WAVE_FORMAT_96M16,
+        WAVE_FORMAT_96S16
+    }
+};
+
+PKSPIN_CONNECT
+MMixerAllocatePinConnect(
+    IN PMIXER_CONTEXT MixerContext,
+    ULONG DataFormatSize)
+{
+    return MixerContext->Alloc(sizeof(KSPIN_CONNECT) + DataFormatSize);
+}
+
+MIXER_STATUS
+MMixerGetWaveInfoByIndexAndType(
+    IN  PMIXER_LIST MixerList,
+    IN  ULONG DeviceIndex,
+    IN  ULONG bWaveInType,
+    OUT LPWAVE_INFO *OutWaveInfo)
+{
+    ULONG Index = 0;
+    PLIST_ENTRY Entry, ListHead;
+    LPWAVE_INFO WaveInfo;
+
+    if (bWaveInType)
+        ListHead = &MixerList->WaveInList;
+    else
+        ListHead = &MixerList->WaveOutList;
+
+    /* get first entry */
+    Entry = ListHead->Flink;
+
+    while(Entry != ListHead)
+    {
+        WaveInfo = (LPWAVE_INFO)CONTAINING_RECORD(Entry, WAVE_INFO, Entry);
+
+        if (Index == DeviceIndex)
+        {
+            *OutWaveInfo = WaveInfo;
+            return MM_STATUS_SUCCESS;
+        }
+        Index++;
+        Entry = Entry->Flink;
+    }
+
+    return MM_STATUS_INVALID_PARAMETER;
+}
+
+
+VOID
+MMixerInitializePinConnect(
+    IN OUT PKSPIN_CONNECT PinConnect,
+    IN ULONG PinId)
+{
+    PinConnect->Interface.Set = KSINTERFACESETID_Standard;
+    PinConnect->Interface.Id = KSINTERFACE_STANDARD_STREAMING;
+    PinConnect->Interface.Flags = 0;
+    PinConnect->Medium.Set = KSMEDIUMSETID_Standard;
+    PinConnect->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE;
+    PinConnect->Medium.Flags = 0;
+    PinConnect->PinToHandle = NULL;
+    PinConnect->PinId = PinId;
+    PinConnect->Priority.PriorityClass = KSPRIORITY_NORMAL;
+    PinConnect->Priority.PrioritySubClass = 1;
+}
+
+VOID
+MMixerInitializeDataFormat(
+    IN PKSDATAFORMAT_WAVEFORMATEX DataFormat,
+    LPWAVEFORMATEX WaveFormatEx)
+{
+
+    DataFormat->WaveFormatEx.wFormatTag = WaveFormatEx->wFormatTag;
+    DataFormat->WaveFormatEx.nChannels = WaveFormatEx->nChannels;
+    DataFormat->WaveFormatEx.nSamplesPerSec = WaveFormatEx->nSamplesPerSec;
+    DataFormat->WaveFormatEx.nBlockAlign = WaveFormatEx->nBlockAlign;
+    DataFormat->WaveFormatEx.nAvgBytesPerSec = WaveFormatEx->nAvgBytesPerSec;
+    DataFormat->WaveFormatEx.wBitsPerSample = WaveFormatEx->wBitsPerSample;
+    DataFormat->WaveFormatEx.cbSize = 0;
+    DataFormat->DataFormat.FormatSize = sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATEX);
+    DataFormat->DataFormat.Flags = 0;
+    DataFormat->DataFormat.Reserved = 0;
+    DataFormat->DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO;
+
+    DataFormat->DataFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+    DataFormat->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
+    DataFormat->DataFormat.SampleSize = 4;
+}
+
+
+MIXER_STATUS
+MMixerGetAudioPinDataRanges(
+    IN PMIXER_CONTEXT MixerContext,
+    IN HANDLE hDevice,
+    IN ULONG PinId,
+    IN OUT PKSMULTIPLE_ITEM * OutMultipleItem)
+{
+    KSP_PIN PinProperty;
+    ULONG BytesReturned = 0;
+    MIXER_STATUS Status;
+    PKSMULTIPLE_ITEM MultipleItem;
+
+    /* retrieve size of data ranges buffer */
+    PinProperty.Reserved = 0;
+    PinProperty.PinId = PinId;
+    PinProperty.Property.Set = KSPROPSETID_Pin;
+    PinProperty.Property.Id = KSPROPERTY_PIN_DATARANGES;
+    PinProperty.Property.Flags = KSPROPERTY_TYPE_GET;
+
+    Status = MixerContext->Control(hDevice, IOCTL_KS_PROPERTY, (PVOID)&PinProperty, sizeof(KSP_PIN), (PVOID)NULL, 0, &BytesReturned);
+    if (Status != MM_STATUS_MORE_ENTRIES)
+    {
+        return Status;
+    }
+
+    MultipleItem = MixerContext->Alloc(BytesReturned);
+    if (!MultipleItem)
+    {
+        /* not enough memory */
+        return MM_STATUS_NO_MEMORY;
+    }
+
+    Status = MixerContext->Control(hDevice, IOCTL_KS_PROPERTY, (PVOID)&PinProperty, sizeof(KSP_PIN), (PVOID)MultipleItem, BytesReturned, &BytesReturned);
+    if (Status != MM_STATUS_SUCCESS)
+    {
+        /* failed */
+        MixerContext->Free(MultipleItem);
+        return Status;
+    }
+
+    /* save result */
+    *OutMultipleItem = MultipleItem;
+    return Status;
+}
+
+MIXER_STATUS
+MMixerFindAudioDataRange(
+    PKSMULTIPLE_ITEM MultipleItem,
+    PKSDATARANGE_AUDIO * OutDataRangeAudio)
+{
+    ULONG Index;
+    PKSDATARANGE_AUDIO DataRangeAudio;
+    PKSDATARANGE DataRange;
+
+    DataRange = (PKSDATARANGE) (MultipleItem + 1);
+    for(Index = 0; Index < MultipleItem->Count; Index++)
+    {
+        if (DataRange->FormatSize == sizeof(KSDATARANGE_AUDIO))
+        {
+            DataRangeAudio = (PKSDATARANGE_AUDIO)DataRange;
+            if (IsEqualGUIDAligned(&DataRangeAudio->DataRange.MajorFormat, &KSDATAFORMAT_TYPE_AUDIO) &&
+                IsEqualGUIDAligned(&DataRangeAudio->DataRange.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM) &&
+                IsEqualGUIDAligned(&DataRangeAudio->DataRange.Specifier, &KSDATAFORMAT_SPECIFIER_WAVEFORMATEX))
+            {
+                DPRINT("Min Sample %u Max Sample %u Min Bits %u Max Bits %u Max Channel %u\n", DataRangeAudio->MinimumSampleFrequency, DataRangeAudio->MaximumSampleFrequency,
+                                                         DataRangeAudio->MinimumBitsPerSample, DataRangeAudio->MaximumBitsPerSample, DataRangeAudio->MaximumChannels);
+                *OutDataRangeAudio = DataRangeAudio;
+                return MM_STATUS_SUCCESS;
+            }
+        }
+        DataRange = (PKSDATARANGE)((ULONG_PTR)DataRange + DataRange->FormatSize);
+    }
+    return MM_STATUS_UNSUCCESSFUL;
+}
+
+MIXER_STATUS
+MMixerOpenWavePin(
+    IN PMIXER_CONTEXT MixerContext,
+    IN PMIXER_LIST MixerList,
+    IN ULONG DeviceId,
+    IN ULONG PinId,
+    IN LPWAVEFORMATEX WaveFormatEx,
+    IN ACCESS_MASK DesiredAccess,
+    OUT PHANDLE PinHandle)
+{
+    PKSPIN_CONNECT PinConnect;
+    PKSDATAFORMAT_WAVEFORMATEX DataFormat;
+    LPMIXER_DATA MixerData;
+    NTSTATUS Status;
+
+    MixerData = MMixerGetDataByDeviceId(MixerList, DeviceId);
+    if (!MixerData)
+        return MM_STATUS_INVALID_PARAMETER;
+
+    /* allocate pin connect */
+    PinConnect = MMixerAllocatePinConnect(MixerContext, sizeof(KSDATAFORMAT_WAVEFORMATEX));
+    if (!PinConnect)
+    {
+        /* no memory */
+        return MM_STATUS_NO_MEMORY;
+    }
+
+    /* initialize pin connect struct */
+    MMixerInitializePinConnect(PinConnect, PinId);
+
+    /* get offset to dataformat */
+    DataFormat = (PKSDATAFORMAT_WAVEFORMATEX) (PinConnect + 1);
+    /* initialize with requested wave format */
+    MMixerInitializeDataFormat(DataFormat, WaveFormatEx);
+
+    /* now create the pin */
+    Status = KsCreatePin(MixerData->hDevice, PinConnect, DesiredAccess, PinHandle);
+
+    /* free create info */
+    MixerContext->Free(PinConnect);
+
+    if (Status == STATUS_SUCCESS)
+        return MM_STATUS_SUCCESS;
+    else
+        return MM_STATUS_UNSUCCESSFUL;
+}
+
+VOID
+MMixerCheckFormat(
+    IN PKSDATARANGE_AUDIO DataRangeAudio,
+    IN LPWAVE_INFO WaveInfo,
+    IN ULONG bInput)
+{
+    ULONG Index, SampleFrequency;
+    ULONG Result = 0;
+
+    for(Index = 0; Index < AUDIO_TEST_RANGE; Index++)
+    {
+        SampleFrequency = TestRange[Index].SampleRate;
+
+        if (DataRangeAudio->MinimumSampleFrequency <= SampleFrequency && DataRangeAudio->MaximumSampleFrequency >= SampleFrequency)
+        {
+            /* the audio adapter supports the sample frequency */
+            if (DataRangeAudio->MinimumBitsPerSample <= 8 && DataRangeAudio->MaximumBitsPerSample >= 8)
+            {
+                Result |= TestRange[Index].Bit8Mono;
+
+                if (DataRangeAudio->MaximumChannels > 1)
+                {
+                    /* check if pin supports the sample rate in 8-Bit Stereo */
+                    Result |= TestRange[Index].Bit8Stereo;
+                }
+            }
+
+            if (DataRangeAudio->MinimumBitsPerSample <= 16 && DataRangeAudio->MaximumBitsPerSample >= 16)
+            {
+                /* check if pin supports the sample rate in 16-Bit Mono */
+                Result |= TestRange[Index].Bit16Mono;
+
+
+                if (DataRangeAudio->MaximumChannels > 1)
+                {
+                    /* check if pin supports the sample rate in 16-Bit Stereo */
+                    Result |= TestRange[Index].Bit16Stereo;
+                }
+            }
+        }
+    }
+
+
+    if (bInput)
+        WaveInfo->u.InCaps.dwFormats = Result;
+    else
+        WaveInfo->u.OutCaps.dwFormats = Result;
+
+    DPRINT("Format %lx bInput %u\n", Result, bInput);
+}
+
+MIXER_STATUS
+MMixerInitializeWaveInfo(
+    IN PMIXER_CONTEXT MixerContext,
+    IN PMIXER_LIST MixerList,
+    IN LPMIXER_DATA MixerData,
+    IN LPWSTR DeviceName,
+    IN ULONG bWaveIn,
+    IN ULONG PinId)
+{
+    MIXER_STATUS Status;
+    PKSMULTIPLE_ITEM MultipleItem;
+    PKSDATARANGE_AUDIO DataRangeAudio;
+    LPWAVE_INFO WaveInfo;
+
+    WaveInfo = (LPWAVE_INFO)MixerContext->Alloc(sizeof(WAVE_INFO));
+    if (!WaveInfo)
+        return MM_STATUS_NO_MEMORY;
+
+    /* initialize wave info */
+    WaveInfo->DeviceId = MixerData->DeviceId;
+    WaveInfo->PinId = PinId;
+
+    /* FIXME determine manufacturer / product id */
+    if (bWaveIn)
+    {
+        WaveInfo->u.InCaps.wMid = MM_MICROSOFT;
+        WaveInfo->u.InCaps.wPid = MM_PID_UNMAPPED;
+        WaveInfo->u.InCaps.vDriverVersion = 1;
+    }
+    else
+    {
+        WaveInfo->u.OutCaps.wMid = MM_MICROSOFT;
+        WaveInfo->u.OutCaps.wPid = MM_PID_UNMAPPED;
+        WaveInfo->u.OutCaps.vDriverVersion = 1;
+    }
+
+    /* get audio pin data ranges */
+    Status = MMixerGetAudioPinDataRanges(MixerContext, MixerData->hDevice, PinId, &MultipleItem);
+    if (Status != MM_STATUS_SUCCESS)
+    {
+        /* failed to get audio pin data ranges */
+        MixerContext->Free(WaveInfo);
+        return MM_STATUS_UNSUCCESSFUL;
+    }
+
+    /* find an KSDATARANGE_AUDIO range */
+    Status = MMixerFindAudioDataRange(MultipleItem, &DataRangeAudio);
+    if (Status != MM_STATUS_SUCCESS)
+    {
+        /* failed to find audio pin data range */
+        MixerContext->Free(MultipleItem);
+        MixerContext->Free(WaveInfo);
+        return MM_STATUS_UNSUCCESSFUL;
+    }
+
+    /* store channel count */
+    if (bWaveIn)
+    {
+        WaveInfo->u.InCaps.wChannels = DataRangeAudio->MaximumChannels;
+    }
+    else
+    {
+       WaveInfo->u.OutCaps.wChannels = DataRangeAudio->MaximumChannels;
+    }
+
+    /* get all supported formats */
+    MMixerCheckFormat(DataRangeAudio, WaveInfo, bWaveIn);
+
+    /* free dataranges buffer */
+    MixerContext->Free(MultipleItem);
+
+
+    if (bWaveIn)
+    {
+        InsertTailList(&MixerList->WaveInList, &WaveInfo->Entry);
+        MixerList->WaveInListCount++;
+    }
+    else
+    {
+        InsertTailList(&MixerList->WaveOutList, &WaveInfo->Entry);
+        MixerList->WaveOutListCount++;
+    }
+
+    return MM_STATUS_SUCCESS;
+}
+
+MIXER_STATUS
+MMixerOpenWave(
+    IN PMIXER_CONTEXT MixerContext,
+    IN ULONG DeviceIndex,
+    IN ULONG bWaveIn,
+    IN LPWAVEFORMATEX WaveFormat,
+    OUT PHANDLE PinHandle)
+{
+    PMIXER_LIST MixerList;
+    MIXER_STATUS Status;
+    LPWAVE_INFO WaveInfo;
+    ACCESS_MASK DesiredAccess = 0;
+
+    // verify mixer context
+    Status = MMixerVerifyContext(MixerContext);
+
+    if (Status != MM_STATUS_SUCCESS)
+    {
+        // invalid context passed
+        return Status;
+    }
+
+    // grab mixer list
+    MixerList = (PMIXER_LIST)MixerContext->MixerContext;
+
+    if (WaveFormat->wFormatTag != WAVE_FORMAT_PCM)
+    {
+        // not implemented
+        return MM_STATUS_NOT_IMPLEMENTED;
+    }
+
+    /* find destination wave */
+    Status = MMixerGetWaveInfoByIndexAndType(MixerList, DeviceIndex, bWaveIn, &WaveInfo);
+    if (Status != MM_STATUS_SUCCESS)
+    {
+        /* failed to find wave info */
+        return MM_STATUS_INVALID_PARAMETER;
+    }
+
+    /* get desired access */
+    if (bWaveIn)
+    {
+        DesiredAccess |= GENERIC_READ;
+    }
+     else
+    {
+        DesiredAccess |= GENERIC_WRITE;
+    }
+
+    /* now try open the pin */
+    return MMixerOpenWavePin(MixerContext, MixerList, WaveInfo->DeviceId, WaveInfo->PinId, WaveFormat, DesiredAccess, PinHandle);
+}
+
+MIXER_STATUS
+MMixerWaveInCapabilities(
+    IN PMIXER_CONTEXT MixerContext,
+    IN ULONG DeviceIndex,
+    OUT LPWAVEINCAPSW Caps)
+{
+    PMIXER_LIST MixerList;
+    MIXER_STATUS Status;
+    LPWAVE_INFO WaveInfo;
+
+    // verify mixer context
+    Status = MMixerVerifyContext(MixerContext);
+
+    if (Status != MM_STATUS_SUCCESS)
+    {
+        // invalid context passed
+        return Status;
+    }
+
+    // grab mixer list
+    MixerList = (PMIXER_LIST)MixerContext->MixerContext;
+
+    /* find destination wave */
+    Status = MMixerGetWaveInfoByIndexAndType(MixerList, DeviceIndex, TRUE, &WaveInfo);
+    if (Status != MM_STATUS_SUCCESS)
+    {
+        /* failed to find wave info */
+        return MM_STATUS_UNSUCCESSFUL;
+    }
+
+    //copy capabilities
+    MixerContext->Copy(Caps, &WaveInfo->u.InCaps, sizeof(WAVEINCAPSW));
+
+    return MM_STATUS_SUCCESS;
+}
+
+MIXER_STATUS
+MMixerWaveOutCapabilities(
+    IN PMIXER_CONTEXT MixerContext,
+    IN ULONG DeviceIndex,
+    OUT LPWAVEOUTCAPSW Caps)
+{
+    PMIXER_LIST MixerList;
+    MIXER_STATUS Status;
+    LPWAVE_INFO WaveInfo;
+
+    // verify mixer context
+    Status = MMixerVerifyContext(MixerContext);
+
+    if (Status != MM_STATUS_SUCCESS)
+    {
+        // invalid context passed
+        return Status;
+    }
+
+    // grab mixer list
+    MixerList = (PMIXER_LIST)MixerContext->MixerContext;
+
+    /* find destination wave */
+    Status = MMixerGetWaveInfoByIndexAndType(MixerList, DeviceIndex, FALSE, &WaveInfo);
+    if (Status != MM_STATUS_SUCCESS)
+    {
+        /* failed to find wave info */
+        return MM_STATUS_UNSUCCESSFUL;
+    }
+
+    //copy capabilities
+    MixerContext->Copy(Caps, &WaveInfo->u.OutCaps, sizeof(WAVEOUTCAPSW));
+
+    return MM_STATUS_SUCCESS;
+}
+
+ULONG
+MMixerGetWaveInCount(
+    IN PMIXER_CONTEXT MixerContext)
+{
+    PMIXER_LIST MixerList;
+    MIXER_STATUS Status;
+
+    // verify mixer context
+    Status = MMixerVerifyContext(MixerContext);
+
+    if (Status != MM_STATUS_SUCCESS)
+    {
+        // invalid context passed
+        return 0;
+    }
+
+    // grab mixer list
+    MixerList = (PMIXER_LIST)MixerContext->MixerContext;
+
+    return MixerList->WaveInListCount;
+}
+
+ULONG
+MMixerGetWaveOutCount(
+    IN PMIXER_CONTEXT MixerContext)
+{
+    PMIXER_LIST MixerList;
+    MIXER_STATUS Status;
+
+    // verify mixer context
+    Status = MMixerVerifyContext(MixerContext);
+
+    if (Status != MM_STATUS_SUCCESS)
+    {
+        // invalid context passed
+        return 0;
+    }
+
+    // grab mixer list
+    MixerList = (PMIXER_LIST)MixerContext->MixerContext;
+
+    return MixerList->WaveOutListCount;
+}