Abandoning silverblade-audio branch.
authorAndrew Greenwood <silverblade@reactos.org>
Sat, 31 Jan 2009 22:11:09 +0000 (22:11 +0000)
committerAndrew Greenwood <silverblade@reactos.org>
Sat, 31 Jan 2009 22:11:09 +0000 (22:11 +0000)
General user-mode audio support libraries added.

svn path=/trunk/; revision=39252

36 files changed:
reactos/lib/drivers/sound/legacy/devname.c [new file with mode: 0644]
reactos/lib/drivers/sound/legacy/hardware.c [new file with mode: 0644]
reactos/lib/drivers/sound/legacy/legacy.rbuild [new file with mode: 0644]
reactos/lib/drivers/sound/mmebuddy/auxiliary/auxMessage.c [new file with mode: 0644]
reactos/lib/drivers/sound/mmebuddy/capabilities.c [new file with mode: 0644]
reactos/lib/drivers/sound/mmebuddy/deviceinstance.c [new file with mode: 0644]
reactos/lib/drivers/sound/mmebuddy/devicelist.c [new file with mode: 0644]
reactos/lib/drivers/sound/mmebuddy/functiontable.c [new file with mode: 0644]
reactos/lib/drivers/sound/mmebuddy/kernel.c [new file with mode: 0644]
reactos/lib/drivers/sound/mmebuddy/midi/midMessage.c [new file with mode: 0644]
reactos/lib/drivers/sound/mmebuddy/midi/modMessage.c [new file with mode: 0644]
reactos/lib/drivers/sound/mmebuddy/mixer/mxdMessage.c [new file with mode: 0644]
reactos/lib/drivers/sound/mmebuddy/mmebuddy.rbuild [new file with mode: 0644]
reactos/lib/drivers/sound/mmebuddy/mmewrap.c [new file with mode: 0644]
reactos/lib/drivers/sound/mmebuddy/reentrancy.c [new file with mode: 0644]
reactos/lib/drivers/sound/mmebuddy/thread.c [new file with mode: 0644]
reactos/lib/drivers/sound/mmebuddy/utility.c [new file with mode: 0644]
reactos/lib/drivers/sound/mmebuddy/wave/format.c [new file with mode: 0644]
reactos/lib/drivers/sound/mmebuddy/wave/header.c [new file with mode: 0644]
reactos/lib/drivers/sound/mmebuddy/wave/widMessage.c [new file with mode: 0644]
reactos/lib/drivers/sound/mmebuddy/wave/wodMessage.c [new file with mode: 0644]
reactos/lib/drivers/sound/mment4/control.c [new file with mode: 0644]
reactos/lib/drivers/sound/mment4/detect.c [new file with mode: 0644]
reactos/lib/drivers/sound/mment4/mment4.rbuild [new file with mode: 0644]
reactos/lib/drivers/sound/mment4/registry.c [new file with mode: 0644]
reactos/lib/drivers/sound/shared/shared.rbuild [new file with mode: 0644]
reactos/lib/drivers/sound/shared/time.c [new file with mode: 0644]
reactos/lib/drivers/sound/sound.rbuild [new file with mode: 0644]
reactos/lib/drivers/sound/soundblaster/dsp_io.c [new file with mode: 0644]
reactos/lib/drivers/sound/soundblaster/mixer.c [new file with mode: 0644]
reactos/lib/drivers/sound/soundblaster/rate.c [new file with mode: 0644]
reactos/lib/drivers/sound/soundblaster/soundblaster.rbuild [new file with mode: 0644]
reactos/lib/drivers/sound/soundblaster/speaker.c [new file with mode: 0644]
reactos/lib/drivers/sound/soundblaster/version.c [new file with mode: 0644]
reactos/lib/drivers/sound/uartmidi/midiuart.c [new file with mode: 0644]
reactos/lib/drivers/sound/uartmidi/uartmidi.rbuild [new file with mode: 0644]

diff --git a/reactos/lib/drivers/sound/legacy/devname.c b/reactos/lib/drivers/sound/legacy/devname.c
new file mode 100644 (file)
index 0000000..c21814e
--- /dev/null
@@ -0,0 +1,480 @@
+/*
+    ReactOS Sound System
+    Device naming & creation helper routines
+
+    Author:
+        Andrew Greenwood (silverblade@reactos.org)
+
+    History:
+        25 May 2008 - Created
+*/
+
+#include <ntddk.h>
+#include <ntddsnd.h>
+#include <debug.h>
+
+
+/*
+    Default device names
+
+    Just to keep things tidy, we define a structure to hold both the \\Device
+    and \\DosDevices names, and then fill this structure with the default
+    device names that can be found in NTDDSND.H
+*/
+
+typedef struct _DEVICE_NAME_GROUP
+{
+    PCWSTR DeviceName;
+    PCWSTR DosDeviceName;
+} DEVICE_NAME_GROUP;
+
+DEVICE_NAME_GROUP SoundDeviceNameBodies[6] =
+{
+    {
+        DD_WAVE_IN_DEVICE_NAME_U,
+        DD_WAVE_IN_DOS_DEVICE_NAME_U
+    },
+    {
+        DD_WAVE_OUT_DEVICE_NAME_U,
+        DD_WAVE_OUT_DOS_DEVICE_NAME_U
+    },
+    {
+        DD_MIDI_IN_DEVICE_NAME_U,
+        DD_MIDI_IN_DOS_DEVICE_NAME_U
+    },
+    {
+        DD_MIDI_OUT_DEVICE_NAME_U,
+        DD_MIDI_OUT_DOS_DEVICE_NAME_U
+    },
+    {
+        DD_MIX_DEVICE_NAME_U,
+        DD_MIX_DOS_DEVICE_NAME_U
+    },
+    {
+        DD_AUX_DEVICE_NAME_U,
+        DD_AUX_DOS_DEVICE_NAME_U
+    }
+};
+
+
+/*
+    ConstructDeviceName
+
+    This takes a wide-character string containing the device name body (for
+    example, "\\Device\\WaveOut") and appends the device index, forming a
+    string like "\\Device\\WaveOut0", and so on.
+
+    The resulting device name is a unicode string.
+*/
+
+NTSTATUS
+ConstructDeviceName(
+    IN  PCWSTR Path,
+    IN  UCHAR Index,
+    OUT PUNICODE_STRING DeviceName)
+{
+    UNICODE_STRING UnicodePath;
+    UNICODE_STRING UnicodeIndex;
+    WCHAR IndexStringBuffer[5];
+    USHORT Size;
+    USHORT LastCharacterIndex;
+
+    /* Check for NULL parameters */
+    if ( ( ! Path ) || ( ! DeviceName ) )
+    {
+        DPRINT("Unexpected NULL parameter");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Range-check */
+    if ( Index >= SOUND_MAX_DEVICES )
+    {
+        DPRINT("Device index %d out of range", Index);
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Initialise the unicode path string */
+    RtlInitUnicodeString(&UnicodePath, Path);
+
+    /* Calculate the length to hold the full string */
+    Size = UnicodePath.Length +
+           sizeof(IndexStringBuffer) +
+           sizeof(UNICODE_NULL);
+
+    /* Allocate memory for DeviceName */
+    DeviceName->Buffer = ExAllocatePool(PagedPool, Size);
+    DeviceName->MaximumLength = Size;
+
+    if ( ! DeviceName->Buffer )
+    {
+        DPRINT("Couldn't allocate memory for device name string");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* Copy the path */
+    RtlCopyUnicodeString(DeviceName, &UnicodePath);
+
+    /* Convert Index to string and append */
+    UnicodeIndex.Buffer = IndexStringBuffer;
+    UnicodeIndex.MaximumLength = sizeof(IndexStringBuffer);
+
+    RtlIntegerToUnicodeString((ULONG)Index, 10, &UnicodeIndex);
+    RtlAppendUnicodeStringToString(DeviceName, &UnicodeIndex);
+
+    /* Terminate the string */
+    LastCharacterIndex = DeviceName->Length / sizeof(UNICODE_NULL);
+    DeviceName->Buffer[LastCharacterIndex] = UNICODE_NULL;
+
+    return STATUS_SUCCESS;
+}
+
+
+/*
+    FreeUnicodeStringBuffer
+
+    A small helper routine to free a unicode string buffer, nullify the
+    buffer and reset the lengths to zero.
+*/
+
+VOID
+FreeUnicodeStringBuffer(IN PUNICODE_STRING String)
+{
+    ASSERT(String != NULL);
+    ASSERT(String->Buffer != NULL);
+
+    ExFreePool(String->Buffer);
+
+    String->Buffer = NULL;
+    String->Length = 0;
+    String->MaximumLength = 0;
+}
+
+
+/*
+    GetDefaultSoundDeviceNameBodies
+
+    Simply accesses the SoundDeviceNameBodies struct defined earlier and
+    fills the DeviceNameBody and DosDeviceNameBody parameters accordingly.
+
+    Basically a "safe" way to access the array and perform two assignments
+    with one call, as this will assign the name and DOS name if a valid
+    DeviceType is passed, otherwise it will fail with STATUS_INVALID_PARAMETER.
+*/
+
+NTSTATUS
+GetDefaultSoundDeviceNameBodies(
+    IN  UCHAR DeviceType,
+    OUT PCWSTR* DeviceNameBody,
+    OUT PCWSTR* DosDeviceNameBody)
+{
+    if ( ! VALID_SOUND_DEVICE_TYPE(DeviceType) )
+    {
+        DPRINT("Invalid device type");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    if ( DeviceNameBody )
+    {
+        DPRINT("Reporting device name\n");
+        *DeviceNameBody = SoundDeviceNameBodies[DeviceType].DeviceName;
+        DPRINT("%ws\n", *DeviceNameBody);
+    }
+
+    if ( DosDeviceNameBody )
+    {
+        DPRINT("Reporting DOS device name\n");
+        *DosDeviceNameBody = SoundDeviceNameBodies[DeviceType].DosDeviceName;
+        DPRINT("%ws\n", *DosDeviceNameBody);
+    }
+
+    return STATUS_SUCCESS;
+}
+
+
+/*
+    ConstructSoundDeviceNames
+
+    Given two wide-character strings and a device index, convert these into
+    two unicode strings with the index appended to the end.
+
+    This is intended for converting a device name and a DOS device name at
+    the same time.
+*/
+
+NTSTATUS
+ConstructSoundDeviceNames(
+    IN  PCWSTR DeviceNameBody,
+    IN  PCWSTR DosDeviceNameBody,
+    IN  UCHAR Index,
+    OUT PUNICODE_STRING FullDeviceName,
+    OUT PUNICODE_STRING FullDosDeviceName)
+{
+    NTSTATUS Status;
+
+    /* Check for NULL parameters */
+    if ( ( ! DeviceNameBody ) || ( ! DosDeviceNameBody ) ||
+         ( ! FullDeviceName ) || ( ! FullDosDeviceName ) )
+    {
+        DPRINT("Unexpected NULL parameter");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Range-check */
+    if ( Index >= SOUND_MAX_DEVICES )
+    {
+        DPRINT("Device %d exceeds maximum", Index);
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    Status = ConstructDeviceName(DeviceNameBody, Index, FullDeviceName);
+
+    if ( ! NT_SUCCESS(Status) )
+    {
+        /* No need to clean up on failure here */
+        return Status;
+    }
+
+    Status = ConstructDeviceName(DosDeviceNameBody, Index, FullDosDeviceName);
+
+    if ( ! NT_SUCCESS(Status) )
+    {
+        /* We need to free the string we successfully got earlier */
+        FreeUnicodeStringBuffer(FullDeviceName);
+        return Status;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+
+/*
+    CreateSoundDevice
+
+    Creates a device and symbolically-links a DOS device to this. Use this
+    when you want to specify alternative device names to the defaults
+    (eg: "\\Device\\MySoundDev" rather than "\\Device\\WaveOut")
+*/
+
+NTSTATUS
+CreateSoundDevice(
+    IN  PDRIVER_OBJECT DriverObject,
+    IN  PCWSTR WideDeviceName,
+    IN  PCWSTR WideDosDeviceName,
+    IN  UCHAR Index,
+    IN  ULONG ExtensionSize,
+    OUT PDEVICE_OBJECT* DeviceObject)
+{
+    NTSTATUS Status;
+
+    UNICODE_STRING DeviceName;
+    UNICODE_STRING DosDeviceName;
+
+    /* Check for NULL parameters */
+    if ( ( ! DriverObject ) || ( ! DeviceObject ) ||
+         ( ! WideDeviceName ) || ( ! WideDosDeviceName ) )
+    {
+        DPRINT("Unexpected NULL parameter");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Range-check */
+    if ( Index >= SOUND_MAX_DEVICES )
+    {
+        DPRINT("Device index %d exceeds maximum", Index);
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Construct the device and DOS device names */
+    Status = ConstructSoundDeviceNames(WideDeviceName,
+                                       WideDosDeviceName,
+                                       Index,
+                                       &DeviceName,
+                                       &DosDeviceName);
+
+    if ( ! NT_SUCCESS(Status) )
+    {
+        return Status;
+    }
+
+    DPRINT("Creating device %ws\n", DeviceName.Buffer);
+
+    /* Now create the device */
+    Status = IoCreateDevice(DriverObject,
+                            ExtensionSize,
+                            &DeviceName,
+                            FILE_DEVICE_SOUND,
+                            0,
+                            FALSE,
+                            DeviceObject);
+
+    if ( ! NT_SUCCESS(Status) )
+    {
+        /* These will have been allocated by ConstructSoundDeviceNames */
+        FreeUnicodeStringBuffer(&DeviceName);
+        FreeUnicodeStringBuffer(&DosDeviceName);
+
+        return Status;
+    }
+
+    DPRINT("Creating link %ws\n", DosDeviceName.Buffer);
+
+    /* Create a symbolic link for the DOS deviec name */
+    Status = IoCreateSymbolicLink(&DosDeviceName, &DeviceName);
+
+    if ( ! NT_SUCCESS(Status) )
+    {
+        IoDeleteDevice(*DeviceObject);
+
+        /* These will have been allocated by ConstructSoundDeviceNames */
+        FreeUnicodeStringBuffer(&DeviceName);
+        FreeUnicodeStringBuffer(&DosDeviceName);
+
+        return Status;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+
+/*
+    CreateSoundDeviceWithDefaultName
+
+    Similar to CreateSoundDevice, except this uses the default device names
+    ("\\Device\\WaveOut" etc.) based on the DeviceType parameter.
+*/
+
+NTSTATUS
+CreateSoundDeviceWithDefaultName(
+    IN  PDRIVER_OBJECT DriverObject,
+    IN  UCHAR DeviceType,
+    IN  UCHAR Index,
+    IN  ULONG ExtensionSize,
+    OUT PDEVICE_OBJECT* DeviceObject)
+{
+    NTSTATUS Status;
+    PCWSTR WideDeviceName = NULL;
+    PCWSTR WideDosDeviceName = NULL;
+
+    /* Check for NULL parameters */
+    if ( ( ! DriverObject ) || ( ! DeviceObject ) )
+    {
+        DPRINT("Unexpected NULL parameter");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Range-check */
+    if ( Index >= SOUND_MAX_DEVICES )
+    {
+        DPRINT("Device index %d exceeds maximum", Index);
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Look-up the default name based on the device type */
+    Status = GetDefaultSoundDeviceNameBodies(DeviceType,
+                                             &WideDeviceName,
+                                             &WideDosDeviceName);
+
+    if ( ! NT_SUCCESS(Status) )
+    {
+        return Status;
+    }
+
+    /* Go create the device! */
+    Status = CreateSoundDevice(DriverObject,
+                               WideDeviceName,
+                               WideDosDeviceName,
+                               Index,
+                               ExtensionSize,
+                               DeviceObject);
+
+    if ( ! NT_SUCCESS(Status) )
+    {
+        /* No clean-up to do */
+        return Status;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+DestroySoundDevice(
+    IN  PDEVICE_OBJECT DeviceObject,
+    IN  PCWSTR WideDosDeviceName,
+    IN  UCHAR Index)
+{
+    NTSTATUS Status;
+    UNICODE_STRING DosDeviceName;
+
+    /* Check for NULL parameters */
+    if ( ( ! WideDosDeviceName ) || ( ! DeviceObject ) )
+    {
+        DPRINT("Unexpected NULL parameter");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Range-check */
+    if ( Index >= SOUND_MAX_DEVICES )
+    {
+        DPRINT("Device %d exceeds maximum", Index);
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    Status = ConstructDeviceName(WideDosDeviceName, Index, &DosDeviceName);
+
+    if ( ! NT_SUCCESS(Status) )
+    {
+        return Status;
+    }
+
+    DPRINT("Deleting symlink %ws\n", DosDeviceName.Buffer);
+
+    Status = IoDeleteSymbolicLink(&DosDeviceName);
+    DPRINT("Status of symlink deletion is 0x%08x\n", Status);
+/*
+    ASSERT(NT_SUCCESS(Status));
+*/
+
+    IoDeleteDevice(DeviceObject);
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+DestroySoundDeviceWithDefaultName(
+    IN  PDEVICE_OBJECT DeviceObject,
+    IN  UCHAR DeviceType,
+    IN  UCHAR Index)
+{
+    NTSTATUS Status;
+    PCWSTR WideDosDeviceName = NULL;
+
+    /* Check for NULL parameters */
+    if ( ( ! DeviceObject ) )
+    {
+        DPRINT("Unexpected NULL parameter");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Range-check */
+    if ( Index >= SOUND_MAX_DEVICES )
+    {
+        DPRINT("Device index %d exceeds maximum", Index);
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Look-up the default name based on the device type */
+    Status = GetDefaultSoundDeviceNameBodies(DeviceType,
+                                             NULL,
+                                             &WideDosDeviceName);
+
+    if ( ! NT_SUCCESS(Status) )
+    {
+        return Status;
+    }
+
+    DPRINT("DOS device name at %p\n", WideDosDeviceName);
+
+    DPRINT("DOS device name is based on %ws\n", WideDosDeviceName);
+
+    return DestroySoundDevice(DeviceObject, WideDosDeviceName, Index);
+}
diff --git a/reactos/lib/drivers/sound/legacy/hardware.c b/reactos/lib/drivers/sound/legacy/hardware.c
new file mode 100644 (file)
index 0000000..4b70ebc
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+    ReactOS Sound System
+    Hardware interaction helper
+
+    Author:
+        Andrew Greenwood (silverblade@reactos.org)
+
+    History:
+        25 May 2008 - Created
+
+    Notes:
+        This uses some obsolete calls (eg: HalGetInterruptVector).
+        Might be worth updating this in future to use some of the
+        recommended functions like IoReportDetectedDevice and
+        IoReportResourceForDetection...
+*/
+
+#include <ntddk.h>
+#include <ntddsnd.h>
+#include <debug.h>
+
+/* NOTE: Disconnect using IoDisconnectInterrupt */
+
+NTSTATUS
+LegacyAttachInterrupt(
+    IN  PDEVICE_OBJECT DeviceObject,
+    IN  UCHAR Irq,
+    IN  PKSERVICE_ROUTINE ServiceRoutine,
+    OUT PKINTERRUPT* InterruptObject)
+{
+    NTSTATUS Status;
+    ULONG Vector;
+    KIRQL IrqLevel;
+    KAFFINITY Affinity;
+
+    DPRINT("Obtaining interrupt vector");
+
+    Vector = HalGetInterruptVector(Isa,
+                                   0,
+                                   Irq,
+                                   Irq,
+                                   &IrqLevel,
+                                   &Affinity);
+
+    DPRINT("Vector %d", Vector);
+    DPRINT("Connecting IRQ %d", Irq);
+
+    Status = IoConnectInterrupt(InterruptObject,
+                                ServiceRoutine,
+                                DeviceObject,
+                                NULL,
+                                Vector,
+                                IrqLevel,
+                                IrqLevel,
+                                Latched,
+                                FALSE,
+                                Affinity,
+                                FALSE);
+
+    if ( Status == STATUS_INVALID_PARAMETER )
+    {
+        Status = STATUS_DEVICE_CONFIGURATION_ERROR;
+    }
+
+    return Status;
+}
diff --git a/reactos/lib/drivers/sound/legacy/legacy.rbuild b/reactos/lib/drivers/sound/legacy/legacy.rbuild
new file mode 100644 (file)
index 0000000..1c83aac
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<!DOCTYPE module SYSTEM "../../../../tools/rbuild/project.dtd">
+<module name="audioleg" type="staticlibrary" allowwarnings="true">
+    <define name="__NTDRIVER__"/>
+    <define name="KERNEL"/>
+    <include base="soundblaster">.</include>
+    <include base="ReactOS">include/reactos/libs/sound</include>
+    <file>devname.c</file>
+    <file>hardware.c</file>
+</module>
diff --git a/reactos/lib/drivers/sound/mmebuddy/auxiliary/auxMessage.c b/reactos/lib/drivers/sound/mmebuddy/auxiliary/auxMessage.c
new file mode 100644 (file)
index 0000000..917b110
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * PROJECT:     ReactOS Sound System "MME Buddy" Library
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        lib/sound/mmebuddy/auxiliary/auxMessage.c
+ *
+ * PURPOSE:     Provides the auxMessage exported function, as required by
+ *              the MME API, for auxiliary device support.
+ *
+ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
+*/
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+
+#include <ntddsnd.h>
+
+#include <mmebuddy.h>
+
+/*
+    Standard MME driver entry-point for messages relating to auxiliary devices.
+*/
+APIENTRY DWORD
+auxMessage(
+    DWORD DeviceId,
+    DWORD Message,
+    DWORD PrivateHandle,
+    DWORD Parameter1,
+    DWORD Parameter2)
+{
+    MMRESULT Result = MMSYSERR_NOTSUPPORTED;
+
+    AcquireEntrypointMutex(AUX_DEVICE_TYPE);
+
+    SND_TRACE(L"auxMessage - Message type %d\n", Message);
+
+    switch ( Message )
+    {
+        case AUXDM_GETNUMDEVS :
+        {
+            Result = GetSoundDeviceCount(AUX_DEVICE_TYPE);
+            break;
+        }
+
+        case AUXDM_GETDEVCAPS :
+        {
+            Result = MmeGetSoundDeviceCapabilities(AUX_DEVICE_TYPE,
+                                                   DeviceId,
+                                                   (PVOID) Parameter1,
+                                                   Parameter2);
+            break;
+        }
+    }
+
+    SND_TRACE(L"auxMessage returning MMRESULT %d\n", Result);
+
+    ReleaseEntrypointMutex(AUX_DEVICE_TYPE);
+
+    return Result;
+}
diff --git a/reactos/lib/drivers/sound/mmebuddy/capabilities.c b/reactos/lib/drivers/sound/mmebuddy/capabilities.c
new file mode 100644 (file)
index 0000000..1bca7cd
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * PROJECT:     ReactOS Sound System "MME Buddy" Library
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        lib/drivers/sound/mmebuddy/capabilities.c
+ *
+ * PURPOSE:     Queries sound devices for their capabilities.
+ *
+ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
+*/
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+#include <ntddsnd.h>
+#include <mmebuddy.h>
+
+/*
+    Obtains the capabilities of a sound device. This routine ensures that the
+    supplied CapabilitiesSize parameter at least meets the minimum size of the
+    relevant capabilities structure.
+
+    Ultimately, it will call the GetCapabilities function specified in the
+    sound device's function table. Note that there are several of these, in a
+    union. This is simply to avoid manually typecasting when implementing the
+    functions.
+*/
+MMRESULT
+GetSoundDeviceCapabilities(
+    IN  PSOUND_DEVICE SoundDevice,
+    OUT PVOID Capabilities,
+    IN  DWORD CapabilitiesSize)
+{
+    MMDEVICE_TYPE DeviceType;
+    PMMFUNCTION_TABLE FunctionTable;
+    BOOLEAN GoodSize = FALSE;
+    MMRESULT Result;
+
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDevice(SoundDevice) );
+    VALIDATE_MMSYS_PARAMETER( Capabilities );
+    VALIDATE_MMSYS_PARAMETER( CapabilitiesSize > 0 );
+
+    /* Obtain the device type */
+    Result = GetSoundDeviceType(SoundDevice, &DeviceType);
+    SND_ASSERT( Result == MMSYSERR_NOERROR );
+
+    if ( ! MMSUCCESS(Result) )
+        return TranslateInternalMmResult(Result);
+
+    /* Obtain the function table */
+    Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
+    SND_ASSERT( Result == MMSYSERR_NOERROR );
+
+    if ( ! MMSUCCESS(Result) )
+        return TranslateInternalMmResult(Result);
+
+    /* Check that the capabilities structure is of a valid size */
+    switch ( DeviceType )
+    {
+        case WAVE_OUT_DEVICE_TYPE :
+        {
+            GoodSize = CapabilitiesSize >= sizeof(WAVEOUTCAPS);
+            break;
+        }
+        case WAVE_IN_DEVICE_TYPE :
+        {
+            GoodSize = CapabilitiesSize >= sizeof(WAVEINCAPS);
+            break;
+        }
+        case MIDI_OUT_DEVICE_TYPE :
+        {
+            GoodSize = CapabilitiesSize >= sizeof(MIDIOUTCAPS);
+            break;
+        }
+        case MIDI_IN_DEVICE_TYPE :
+        {
+            GoodSize = CapabilitiesSize >= sizeof(MIDIINCAPS);
+            break;
+        }
+        /* TODO: Others... */
+        default :
+        {
+            SND_ASSERT(FALSE);
+        }
+    };
+
+    if ( ! GoodSize )
+    {
+        SND_ERR(L"Device capabilities structure too small\n");
+        return MMSYSERR_INVALPARAM;
+    }
+
+    /* Call the "get capabilities" function within the function table */
+    SND_ASSERT( FunctionTable->GetCapabilities );
+
+    if ( ! FunctionTable->GetCapabilities )
+        return MMSYSERR_NOTSUPPORTED;
+
+    return FunctionTable->GetCapabilities(SoundDevice,
+                                          Capabilities,
+                                          CapabilitiesSize);
+}
diff --git a/reactos/lib/drivers/sound/mmebuddy/deviceinstance.c b/reactos/lib/drivers/sound/mmebuddy/deviceinstance.c
new file mode 100644 (file)
index 0000000..e6d0029
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ * PROJECT:     ReactOS Sound System "MME Buddy" Library
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        lib/drivers/sound/mmebuddy/deviceinstance.c
+ *
+ * PURPOSE:     Manages instances of sound devices.
+ *
+ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
+*/
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+#include <mmebuddy.h>
+
+MMRESULT
+AllocateSoundDeviceInstance(
+    OUT PSOUND_DEVICE_INSTANCE* SoundDeviceInstance)
+{
+    PSOUND_DEVICE_INSTANCE NewInstance;
+
+    VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance );
+
+    /* Allocate memory for the new instance */
+    NewInstance = AllocateStruct(SOUND_DEVICE_INSTANCE);
+
+    if ( ! NewInstance )
+        return MMSYSERR_NOMEM;
+
+    /* Provide the caller with the new instance pointer */
+    *SoundDeviceInstance = NewInstance;
+
+    return MMSYSERR_NOERROR;
+}
+
+VOID
+FreeSoundDeviceInstance(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
+{
+    /* This won't work as the device is no longer valid by this point! */
+    /*SND_ASSERT( IsValidSoundDeviceInstance(SoundDeviceInstance) );*/
+    ZeroMemory(SoundDeviceInstance, sizeof(SOUND_DEVICE_INSTANCE));
+    FreeMemory(SoundDeviceInstance);
+}
+
+BOOLEAN
+IsValidSoundDeviceInstance(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
+{
+    PSOUND_DEVICE SoundDevice;
+    PSOUND_DEVICE_INSTANCE CurrentInstance;
+
+    if ( ! SoundDeviceInstance )
+        return FALSE;
+
+    /* GetSoundDeviceFromInstance would send us into a recursive loop... */
+    SoundDevice = SoundDeviceInstance->Device;
+    SND_ASSERT(SoundDevice);
+
+    CurrentInstance = SoundDevice->HeadInstance;
+
+    while ( CurrentInstance )
+    {
+        if ( CurrentInstance == SoundDeviceInstance )
+            return TRUE;
+
+        CurrentInstance = CurrentInstance->Next;
+    }
+
+    return FALSE;
+}
+
+MMRESULT
+ListSoundDeviceInstance(
+    IN  PSOUND_DEVICE SoundDevice,
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
+{
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDevice(SoundDevice) );
+    VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance );
+
+    SND_TRACE(L"Listing sound device instance\n");
+
+    if ( ! SoundDevice->HeadInstance )
+    {
+        /* First entry - assign to head and tail */
+        SoundDevice->HeadInstance = SoundDeviceInstance;
+        SoundDevice->TailInstance = SoundDeviceInstance;
+    }
+    else
+    {
+        /* Attach to the end */
+        SoundDevice->TailInstance->Next = SoundDeviceInstance;
+        SoundDevice->TailInstance = SoundDeviceInstance;
+    }
+
+    return MMSYSERR_NOERROR;
+}
+
+MMRESULT
+UnlistSoundDeviceInstance(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
+{
+    MMRESULT Result;
+    PSOUND_DEVICE SoundDevice;
+    PSOUND_DEVICE_INSTANCE CurrentInstance, PreviousInstance;
+
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
+
+    SND_TRACE(L"Unlisting sound device instance\n");
+
+    Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
+    SND_ASSERT( MMSUCCESS(Result) );
+
+    PreviousInstance = NULL;
+    CurrentInstance = SoundDevice->HeadInstance;
+
+    while ( CurrentInstance )
+    {
+        if ( CurrentInstance == SoundDeviceInstance )
+        {
+            if ( ! PreviousInstance )
+            {
+                /* This is the head node */
+                SoundDevice->HeadInstance = SoundDevice->HeadInstance->Next;
+            }
+            else
+            {
+                /* There are nodes before this one - cut ours out */
+                PreviousInstance->Next = CurrentInstance->Next;
+            }
+
+            if ( ! CurrentInstance->Next )
+            {
+                /* This is the tail node */
+                SoundDevice->TailInstance = PreviousInstance;
+            }
+        }
+
+        PreviousInstance = CurrentInstance;
+        CurrentInstance = CurrentInstance->Next;
+    }
+
+    return MMSYSERR_NOERROR;
+}
+
+MMRESULT
+CreateSoundDeviceInstance(
+    IN  PSOUND_DEVICE SoundDevice,
+    OUT PSOUND_DEVICE_INSTANCE* SoundDeviceInstance)
+{
+    MMRESULT Result;
+    PMMFUNCTION_TABLE FunctionTable;
+
+    SND_TRACE(L"Creating a sound device instance\n");
+
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDevice(SoundDevice) );
+    VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance != NULL );
+
+    Result = AllocateSoundDeviceInstance(SoundDeviceInstance);
+
+    if ( ! MMSUCCESS(Result) )
+        return TranslateInternalMmResult(Result);
+
+    /* Get the "open" routine from the function table, and validate it */
+    Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
+    if ( ! MMSUCCESS(Result) )
+    {
+        FreeSoundDeviceInstance(*SoundDeviceInstance);
+        return TranslateInternalMmResult(Result);
+    }
+
+    if ( FunctionTable->Open == NULL )
+    {
+        FreeSoundDeviceInstance(*SoundDeviceInstance);
+        return MMSYSERR_NOTSUPPORTED;
+    }
+
+    /* Set up the members of the structure */
+    (*SoundDeviceInstance)->Next = NULL; 
+    (*SoundDeviceInstance)->Device = SoundDevice;
+    (*SoundDeviceInstance)->Handle = NULL;
+    (*SoundDeviceInstance)->Thread = NULL;
+
+    (*SoundDeviceInstance)->WinMM.Handle = INVALID_HANDLE_VALUE;
+    (*SoundDeviceInstance)->WinMM.ClientCallback = 0;
+    (*SoundDeviceInstance)->WinMM.ClientCallbackInstanceData = 0;
+    (*SoundDeviceInstance)->WinMM.Flags = 0;
+
+    /* Create the streaming thread (TODO - is this for wave only?) */
+    Result = CreateSoundThread(&(*SoundDeviceInstance)->Thread);
+    if ( ! MMSUCCESS(Result) )
+    {
+        FreeSoundDeviceInstance(*SoundDeviceInstance);
+        return TranslateInternalMmResult(Result);
+    }
+
+    /* Add the instance to the list */
+    Result = ListSoundDeviceInstance(SoundDevice, *SoundDeviceInstance);
+    if ( ! MMSUCCESS(Result) )
+    {
+        FreeSoundDeviceInstance(*SoundDeviceInstance);
+        return TranslateInternalMmResult(Result);
+    }
+
+    /* Try and open the device */
+    Result = FunctionTable->Open(SoundDevice, (&(*SoundDeviceInstance)->Handle));
+    if ( ! MMSUCCESS(Result) )
+    {
+        UnlistSoundDeviceInstance(*SoundDeviceInstance);
+        FreeSoundDeviceInstance(*SoundDeviceInstance);
+        return TranslateInternalMmResult(Result);
+    }
+
+    return MMSYSERR_NOERROR;
+}
+
+MMRESULT
+DestroySoundDeviceInstance(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
+{
+    MMRESULT Result;
+    PMMFUNCTION_TABLE FunctionTable;
+    PSOUND_DEVICE SoundDevice;
+    PVOID Handle;
+
+    SND_TRACE(L"Destroying a sound device instance\n");
+
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
+
+    Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
+    if ( ! MMSUCCESS(Result) )
+        return TranslateInternalMmResult(Result);
+
+    Result = GetSoundDeviceInstanceHandle(SoundDeviceInstance, &Handle);
+    if ( ! MMSUCCESS(Result) )
+        return TranslateInternalMmResult(Result);
+
+    /* Get the "close" routine from the function table, and validate it */
+    Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
+    if ( ! MMSUCCESS(Result) )
+        return TranslateInternalMmResult(Result);
+
+    SND_ASSERT( FunctionTable->Close );
+    if ( FunctionTable->Close == NULL )
+    {
+        /* Bad practice, really! If you can open, why not close?! */
+        return MMSYSERR_NOTSUPPORTED;
+    }
+
+    /* Try and close the device */
+    Result = FunctionTable->Close(SoundDeviceInstance, Handle);
+    if ( ! MMSUCCESS(Result) )
+        return TranslateInternalMmResult(Result);
+
+    /* Drop it from the list */
+    Result = UnlistSoundDeviceInstance(SoundDeviceInstance);
+    if ( ! MMSUCCESS(Result) )
+        return TranslateInternalMmResult(Result);
+
+    FreeSoundDeviceInstance(SoundDeviceInstance);
+
+    return MMSYSERR_NOERROR;
+}
+
+MMRESULT
+DestroyAllSoundDeviceInstances(
+    IN  PSOUND_DEVICE SoundDevice)
+{
+    return MMSYSERR_NOTSUPPORTED;
+}
+
+MMRESULT
+GetSoundDeviceFromInstance(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
+    OUT PSOUND_DEVICE* SoundDevice)
+{
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
+    VALIDATE_MMSYS_PARAMETER( SoundDevice );
+
+    *SoundDevice = SoundDeviceInstance->Device;
+
+    return MMSYSERR_NOERROR;
+}
+
+MMRESULT
+GetSoundDeviceInstanceHandle(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
+    OUT PVOID* Handle)
+{
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
+    VALIDATE_MMSYS_PARAMETER( Handle );
+
+    *Handle = SoundDeviceInstance->Handle;
+
+    return MMSYSERR_NOERROR;
+}
+
+MMRESULT
+SetSoundDeviceInstanceMmeData(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
+    IN  HDRVR MmeHandle,
+    IN  DWORD ClientCallback,
+    IN  DWORD ClientCallbackData,
+    IN  DWORD Flags)
+{
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
+
+    SND_TRACE(L"Setting MME data - handle %x, callback %x, instance data %x, flags %x\n",
+              MmeHandle, ClientCallback, ClientCallbackData, Flags);
+
+    SoundDeviceInstance->WinMM.Handle = MmeHandle;
+    SoundDeviceInstance->WinMM.ClientCallback = ClientCallback;
+    SoundDeviceInstance->WinMM.ClientCallbackInstanceData = ClientCallbackData;
+    SoundDeviceInstance->WinMM.Flags = Flags;
+
+    return MMSYSERR_NOERROR;
+}
diff --git a/reactos/lib/drivers/sound/mmebuddy/devicelist.c b/reactos/lib/drivers/sound/mmebuddy/devicelist.c
new file mode 100644 (file)
index 0000000..f16b3ea
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ * PROJECT:     ReactOS Sound System "MME Buddy" Library
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        lib/drivers/sound/mmebuddy/devicelist.c
+ *
+ * PURPOSE:     Manages lists of sound devices.
+ *
+ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
+*/
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+#include <ntddsnd.h>
+#include <mmebuddy.h>
+
+ULONG           SoundDeviceCounts[SOUND_DEVICE_TYPES];
+PSOUND_DEVICE   SoundDeviceListHeads[SOUND_DEVICE_TYPES];
+PSOUND_DEVICE   SoundDeviceListTails[SOUND_DEVICE_TYPES];
+
+/*
+    Handles the allocation and initialisation of a SOUND_DEVICE structure.
+*/
+MMRESULT
+AllocateSoundDevice(
+    IN  MMDEVICE_TYPE DeviceType,
+    OUT PSOUND_DEVICE* SoundDevice)
+{
+    PSOUND_DEVICE NewDevice;
+
+    SND_ASSERT( IsValidSoundDeviceType(DeviceType) );
+    SND_ASSERT( SoundDevice );
+
+    SND_TRACE(L"Allocating a SOUND_DEVICE structure\n");
+
+    NewDevice = AllocateStruct(SOUND_DEVICE);
+
+    if ( ! NewDevice )
+        return MMSYSERR_NOMEM;
+
+    NewDevice->Type = DeviceType;
+
+    /* Return the new structure to the caller and report success */
+    *SoundDevice = NewDevice;
+
+    return MMSYSERR_NOERROR;
+}
+
+/*
+    Handles the cleanup and freeing of a SOUND_DEVICE structure.
+*/
+VOID
+FreeSoundDevice(
+    IN  PSOUND_DEVICE SoundDevice)
+{
+    SND_ASSERT( SoundDevice );
+
+    SND_TRACE(L"Freeing a SOUND_DEVICE structure");
+
+    /* For safety the whole struct gets zeroed */
+    ZeroMemory(SoundDevice, sizeof(SOUND_DEVICE));
+    FreeMemory(SoundDevice);
+}
+
+/*
+    Returns the number of devices of the specified type which have been added
+    to the device lists. If an invalid device type is specified, the function
+    returns zero.
+*/
+ULONG
+GetSoundDeviceCount(
+    IN  MMDEVICE_TYPE DeviceType)
+{
+    ULONG Index = SOUND_DEVICE_TYPE_TO_INDEX(DeviceType);
+
+    if ( ! IsValidSoundDeviceType(DeviceType) )
+    {
+        return 0;
+    }
+
+    SND_TRACE(L"Returning a count of %d devices\n", SoundDeviceCounts[Index]);
+    return SoundDeviceCounts[Index];
+}
+
+/*
+    Determines if a sound device structure pointer is valid, firstly by
+    ensuring that it is not NULL, and then by checking that the device itself
+    exists in one of the device lists.
+*/
+BOOLEAN
+IsValidSoundDevice(
+    IN  PSOUND_DEVICE SoundDevice)
+{
+    UCHAR TypeIndex;
+    PSOUND_DEVICE CurrentDevice;
+
+    if ( ! SoundDevice )
+        return FALSE;
+
+    /* Go through all the device lists */
+    for ( TypeIndex = 0; TypeIndex < SOUND_DEVICE_TYPES; ++ TypeIndex )
+    {
+        CurrentDevice = SoundDeviceListHeads[TypeIndex];
+
+        while ( CurrentDevice )
+        {
+            if ( CurrentDevice == SoundDevice )
+            {
+                /* Found the device */
+                return TRUE;
+            }
+
+            CurrentDevice = CurrentDevice->Next;
+        }
+    }
+
+    /* If we get here, nothing was found */
+    return FALSE;
+}
+
+/*
+    Informs the MME-Buddy library that it should take ownership of a device.
+    The DevicePath is typically used for storing a device path (for subsequent
+    opening using CreateFile) but it can be a wide-string representing any
+    information that makes sense to your MME driver implementation.
+
+    MME components which operate solely in user-mode (for example, MIDI
+    loopback devices) won't need to communicate with a kernel-mode device,
+    so in these situations DevicePath is likely to be NULL.
+
+    Upon successful addition to the sound device list, the pointer to the new
+    device's SOUND_DEVICE structure is returned via SoundDevice.
+*/
+MMRESULT
+ListSoundDevice(
+    IN  MMDEVICE_TYPE DeviceType,
+    IN  PVOID Identifier OPTIONAL,
+    OUT PSOUND_DEVICE* SoundDevice OPTIONAL)
+{
+    MMRESULT Result;
+    PSOUND_DEVICE NewDevice;
+    UCHAR TypeIndex = SOUND_DEVICE_TYPE_TO_INDEX(DeviceType);
+
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceType(DeviceType) );
+
+    Result = AllocateSoundDevice(DeviceType, &NewDevice);
+
+    if ( ! MMSUCCESS(Result) )
+    {
+        SND_ERR(L"Failed to allocate SOUND_DEVICE structure\n");
+        return Result;
+    }
+
+    if ( ! SoundDeviceListHeads[TypeIndex] )
+    {
+        SND_TRACE(L"Putting first entry into device list %d\n", DeviceType);
+        SoundDeviceListHeads[TypeIndex] = NewDevice;
+        SoundDeviceListTails[TypeIndex] = NewDevice;
+    }
+    else
+    {
+        SND_TRACE(L"Putting another entry into device list %d\n", DeviceType);
+        SoundDeviceListTails[TypeIndex]->Next = NewDevice;
+        SoundDeviceListTails[TypeIndex] = NewDevice;
+    }
+
+    /* Add to the count */
+    ++ SoundDeviceCounts[TypeIndex];
+
+    /* Set up the default function table */
+    SetSoundDeviceFunctionTable(NewDevice, NULL);
+
+    /* Set up other members of the structure */
+    NewDevice->Identifier = Identifier;
+    NewDevice->HeadInstance = NULL;
+    NewDevice->TailInstance = NULL;
+
+    /* Fill in the caller's PSOUND_DEVICE */
+    if ( SoundDevice )
+    {
+        *SoundDevice = NewDevice;
+    }
+
+    return MMSYSERR_NOERROR;
+}
+
+/*
+    Removes a sound device from the list, and frees the memory associated
+    with its description.
+*/
+MMRESULT
+UnlistSoundDevice(
+    IN  MMDEVICE_TYPE DeviceType,
+    IN  PSOUND_DEVICE SoundDevice)
+{
+    PSOUND_DEVICE CurrentDevice, PreviousDevice;
+
+    UCHAR TypeIndex = SOUND_DEVICE_TYPE_TO_INDEX(DeviceType);
+
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceType(DeviceType) );
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDevice(SoundDevice) );
+
+    PreviousDevice = NULL;
+    CurrentDevice = SoundDeviceListHeads[TypeIndex];
+
+    while ( CurrentDevice )
+    {
+        if ( CurrentDevice == SoundDevice )
+        {
+            if ( ! PreviousDevice )
+            {
+                /* This is the head node */
+                SND_TRACE(L"Removing head node from device list %d\n", DeviceType);
+                SoundDeviceListHeads[TypeIndex] =
+                    SoundDeviceListHeads[TypeIndex]->Next;
+            }
+            else
+            {
+                SND_TRACE(L"Removing node from device list %d\n", DeviceType);
+                /* There are nodes before this one - cut our device out */
+                PreviousDevice->Next = CurrentDevice->Next;
+            }
+
+            if ( ! CurrentDevice->Next )
+            {
+                /* This is the tail node */
+                SND_TRACE(L"Removing tail node from device list %d\n", DeviceType);
+                SoundDeviceListTails[TypeIndex] = PreviousDevice;
+            }
+        }
+
+        PreviousDevice = CurrentDevice;
+        CurrentDevice = CurrentDevice->Next;
+    }
+
+    /* Subtract from the count */
+    -- SoundDeviceCounts[TypeIndex];
+
+    /* Finally, free up the deleted entry */
+    FreeSoundDevice(SoundDevice);
+
+    return MMSYSERR_NOERROR;
+}
+
+/*
+    Removes all devices from one of the device lists.
+*/
+MMRESULT
+UnlistSoundDevices(
+    IN  MMDEVICE_TYPE DeviceType)
+{
+    UCHAR TypeIndex;
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceType(DeviceType) );
+
+    SND_TRACE(L"Unlisting all sound devices of type %d\n", DeviceType);
+
+    TypeIndex = SOUND_DEVICE_TYPE_TO_INDEX(DeviceType);
+
+    /* Munch away at the head of the list until it's drained */
+    while ( SoundDeviceCounts[TypeIndex] > 0 )
+    {
+        MMRESULT Result;
+        Result = UnlistSoundDevice(DeviceType, SoundDeviceListHeads[TypeIndex]);
+        SND_ASSERT( Result == MMSYSERR_NOERROR );
+    }
+
+    return MMSYSERR_NOERROR;
+}
+
+/*
+    Removes all devices from all lists.
+*/
+VOID
+UnlistAllSoundDevices()
+{
+    MMDEVICE_TYPE Type;
+
+    SND_TRACE(L"Unlisting all sound devices\n");
+
+    for ( Type = MIN_SOUND_DEVICE_TYPE; Type <= MAX_SOUND_DEVICE_TYPE; ++ Type )
+    {
+        MMRESULT Result;
+        Result = UnlistSoundDevices(Type);
+        SND_ASSERT( Result == MMSYSERR_NOERROR );
+    }
+}
+
+/*
+    Provides the caller with a pointer to its desired sound device, based on
+    the device type and index.
+*/
+MMRESULT
+GetSoundDevice(
+    IN  MMDEVICE_TYPE DeviceType,
+    IN  DWORD DeviceIndex,
+    OUT PSOUND_DEVICE* SoundDevice)
+{
+    UCHAR TypeIndex = SOUND_DEVICE_TYPE_TO_INDEX(DeviceType);
+    DWORD CurrentIndex = 0;
+    PSOUND_DEVICE CurrentDevice;
+
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceType(DeviceType) );
+
+    if ( DeviceIndex >= SoundDeviceCounts[TypeIndex] )
+    {
+        SND_ERR(L"Invalid device ID %d for type %d\n", DeviceIndex, DeviceType);
+        return MMSYSERR_BADDEVICEID;
+    }
+
+    CurrentDevice = SoundDeviceListHeads[TypeIndex];
+
+    /* Following the earlier checks, the index should be valid here. */
+    for ( CurrentIndex = 0; CurrentIndex != DeviceIndex; ++ CurrentIndex )
+    {
+        SND_ASSERT( CurrentDevice );
+        CurrentDevice = CurrentDevice->Next;
+    }
+
+    SND_TRACE(L"Returning sound device %x\n", CurrentDevice);
+
+    *SoundDevice = CurrentDevice;
+
+    return MMSYSERR_NOERROR;
+}
+
+/*
+    Provides the caller with the device path of the specified sound device.
+    This will normally be the path to a device provided by a kernel-mode
+    driver.
+*/
+MMRESULT
+GetSoundDeviceIdentifier(
+    IN  PSOUND_DEVICE SoundDevice,
+    OUT PVOID* Identifier)
+{
+    VALIDATE_MMSYS_PARAMETER( SoundDevice );
+    VALIDATE_MMSYS_PARAMETER( Identifier );
+
+    /* The caller should not modify this! */
+    *Identifier = SoundDevice->Identifier;
+
+    return MMSYSERR_NOERROR;
+}
+
+/*
+    Provides the caller with the device type of the specified sound device.
+    This will be, for example, WAVE_OUT_DEVICE_TYPE, WAVE_IN_DEVICE_TYPE ...
+*/
+MMRESULT
+GetSoundDeviceType(
+    IN  PSOUND_DEVICE SoundDevice,
+    OUT PMMDEVICE_TYPE DeviceType)
+{
+    VALIDATE_MMSYS_PARAMETER( SoundDevice );
+    VALIDATE_MMSYS_PARAMETER( DeviceType );
+
+    *DeviceType = SoundDevice->Type;
+
+    return MMSYSERR_NOERROR;
+}
diff --git a/reactos/lib/drivers/sound/mmebuddy/functiontable.c b/reactos/lib/drivers/sound/mmebuddy/functiontable.c
new file mode 100644 (file)
index 0000000..bed72db
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * PROJECT:     ReactOS Sound System "MME Buddy" Library
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        lib/drivers/sound/mmebuddy/functiontable.c
+ *
+ * PURPOSE:     Routes function calls through a function table, calling
+ *              implementation-defined routines or a default function, depending
+ *              on configuration.
+ *
+ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
+*/
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+#include <mmebuddy.h>
+
+/*
+    Attaches a function table to a sound device. Any NULL entries in this
+    table are automatically set to point to a default routine to handle
+    the appropriate function. If NULL is passed as the function table itself,
+    the entire function table will use only the default routines.
+*/
+MMRESULT
+SetSoundDeviceFunctionTable(
+    IN  PSOUND_DEVICE SoundDevice,
+    IN  PMMFUNCTION_TABLE FunctionTable OPTIONAL)
+{
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDevice(SoundDevice) );
+
+    /* Zero out the existing function table (if present) */
+    ZeroMemory(&SoundDevice->FunctionTable, sizeof(MMFUNCTION_TABLE));
+
+    if ( FunctionTable )
+    {
+        /* Fill in the client-supplied functions */
+        CopyMemory(&SoundDevice->FunctionTable,
+                   FunctionTable,
+                   sizeof(MMFUNCTION_TABLE));
+    }
+
+    return MMSYSERR_NOERROR;
+}
+
+/*
+    Retrieves the function table for a sound device, as previously set using
+    SetSoundDeviceFunctionTable.
+*/
+MMRESULT
+GetSoundDeviceFunctionTable(
+    IN  PSOUND_DEVICE SoundDevice,
+    OUT PMMFUNCTION_TABLE* FunctionTable)
+{
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDevice(SoundDevice) );
+    VALIDATE_MMSYS_PARAMETER( FunctionTable );
+
+    *FunctionTable = &SoundDevice->FunctionTable;
+
+    return MMSYSERR_NOERROR;
+}
diff --git a/reactos/lib/drivers/sound/mmebuddy/kernel.c b/reactos/lib/drivers/sound/mmebuddy/kernel.c
new file mode 100644 (file)
index 0000000..2815c4b
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * PROJECT:     ReactOS Sound System "MME Buddy" Library
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        lib/drivers/sound/mmebuddy/kernel.c
+ *
+ * PURPOSE:     Routines assisting with device I/O between user-mode and
+ *              kernel-mode.
+ *
+ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
+*/
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+#include <ntddsnd.h>
+#include <mmebuddy.h>
+
+/*
+    Wraps around CreateFile in order to provide a simpler interface tailored
+    towards sound driver support code. This simply takes a device path and
+    opens the device in either read-only mode, or read/write mode (depending on
+    the ReadOnly parameter).
+
+    If the device is opened in read/write mode, it is opened for overlapped I/O.
+*/
+MMRESULT
+OpenKernelSoundDeviceByName(
+    IN  PWSTR DevicePath,
+    IN  BOOLEAN ReadOnly,
+    OUT PHANDLE Handle)
+{
+    DWORD AccessRights;
+
+    VALIDATE_MMSYS_PARAMETER( DevicePath );
+    VALIDATE_MMSYS_PARAMETER( Handle );
+
+    AccessRights = ReadOnly ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE;
+
+    SND_TRACE(L"OpenKernelSoundDeviceByName: %wS\n", DevicePath);
+    *Handle = CreateFile(DevicePath,
+                         AccessRights,
+                         FILE_SHARE_WRITE,  /* FIXME? Should be read also? */
+                         NULL,
+                         OPEN_EXISTING,
+                         ReadOnly ? 0 : FILE_FLAG_OVERLAPPED,
+                         NULL);
+
+    if ( *Handle == INVALID_HANDLE_VALUE )
+    {
+        SND_ERR(L"CreateFile filed - winerror %d\n", GetLastError());
+        return Win32ErrorToMmResult(GetLastError());
+    }
+
+    return MMSYSERR_NOERROR;
+}
+
+
+/*
+    Just a wrapped around CloseHandle.
+*/
+MMRESULT
+CloseKernelSoundDevice(
+    IN  HANDLE Handle)
+{
+    VALIDATE_MMSYS_PARAMETER( Handle );
+
+    CloseHandle(Handle);
+
+    return MMSYSERR_NOERROR;
+}
+
+/*
+    This is a wrapper around DeviceIoControl which provides control over
+    instantiated sound devices. It waits for I/O to complete (since an
+    instantiated sound device is opened in overlapped mode, this is necessary).
+*/
+MMRESULT
+SyncOverlappedDeviceIoControl(
+    IN  HANDLE Handle,
+    IN  DWORD IoControlCode,
+    IN  LPVOID InBuffer,
+    IN  DWORD InBufferSize,
+    OUT LPVOID OutBuffer,
+    IN  DWORD OutBufferSize,
+    OUT LPDWORD BytesTransferred OPTIONAL)
+{
+    OVERLAPPED Overlapped;
+    BOOLEAN IoResult;
+    DWORD Transferred;
+
+    /* Overlapped I/O is done here - this is used for waiting for completion */
+    ZeroMemory(&Overlapped, sizeof(OVERLAPPED));
+    Overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+    if ( ! Overlapped.hEvent )
+        return Win32ErrorToMmResult(GetLastError());
+
+    /* Talk to the device */
+    IoResult = DeviceIoControl(Handle,
+                               IoControlCode,
+                               InBuffer,
+                               InBufferSize,
+                               OutBuffer,
+                               OutBufferSize,
+                               NULL,
+                               &Overlapped);
+
+    /* If failure occurs, make sure it's not just due to the overlapped I/O */
+    if ( ! IoResult )
+    {
+        if ( GetLastError() != ERROR_IO_PENDING )
+        {
+            CloseHandle(Overlapped.hEvent);
+            return Win32ErrorToMmResult(GetLastError());
+        }
+    }
+
+    /* Wait for the I/O to complete */
+    IoResult = GetOverlappedResult(Handle,
+                                   &Overlapped,
+                                   &Transferred,
+                                   TRUE);
+
+    /* Don't need this any more */
+    CloseHandle(Overlapped.hEvent);
+
+    if ( ! IoResult )
+        return Win32ErrorToMmResult(GetLastError());
+
+    if ( BytesTransferred )
+        *BytesTransferred = Transferred;
+
+    return MMSYSERR_NOERROR;
+}
diff --git a/reactos/lib/drivers/sound/mmebuddy/midi/midMessage.c b/reactos/lib/drivers/sound/mmebuddy/midi/midMessage.c
new file mode 100644 (file)
index 0000000..79be406
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * PROJECT:     ReactOS Sound System "MME Buddy" Library
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        lib/sound/mmebuddy/midi/midMessage.c
+ *
+ * PURPOSE:     Provides the midMessage exported function, as required by
+ *              the MME API, for MIDI input device support.
+ *
+ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
+*/
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+
+#include <ntddsnd.h>
+
+#include <mmebuddy.h>
+
+/*
+    Standard MME driver entry-point for messages relating to MIDI input.
+*/
+APIENTRY DWORD
+midMessage(
+    DWORD DeviceId,
+    DWORD Message,
+    DWORD PrivateHandle,
+    DWORD Parameter1,
+    DWORD Parameter2)
+{
+    MMRESULT Result = MMSYSERR_NOTSUPPORTED;
+
+    AcquireEntrypointMutex(MIDI_IN_DEVICE_TYPE);
+
+    SND_TRACE(L"midMessage - Message type %d\n", Message);
+
+    switch ( Message )
+    {
+        case MIDM_GETNUMDEVS :
+        {
+            Result = GetSoundDeviceCount(MIDI_IN_DEVICE_TYPE);
+            break;
+        }
+
+        case MIDM_GETDEVCAPS :
+        {
+            Result = MmeGetSoundDeviceCapabilities(MIDI_IN_DEVICE_TYPE,
+                                                   DeviceId,
+                                                   (PVOID) Parameter1,
+                                                   Parameter2);
+            break;
+        }
+    }
+
+    SND_TRACE(L"midMessage returning MMRESULT %d\n", Result);
+
+    ReleaseEntrypointMutex(MIDI_IN_DEVICE_TYPE);
+
+    return Result;
+}
diff --git a/reactos/lib/drivers/sound/mmebuddy/midi/modMessage.c b/reactos/lib/drivers/sound/mmebuddy/midi/modMessage.c
new file mode 100644 (file)
index 0000000..30990e4
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * PROJECT:     ReactOS Sound System "MME Buddy" Library
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        lib/sound/mmebuddy/midi/modMessage.c
+ *
+ * PURPOSE:     Provides the modMessage exported function, as required by
+ *              the MME API, for MIDI output device support.
+ *
+ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
+*/
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+
+#include <ntddsnd.h>
+
+#include <mmebuddy.h>
+
+/*
+    Standard MME driver entry-point for messages relating to MIDI output.
+*/
+APIENTRY DWORD
+modMessage(
+    DWORD DeviceId,
+    DWORD Message,
+    DWORD PrivateHandle,
+    DWORD Parameter1,
+    DWORD Parameter2)
+{
+    MMRESULT Result = MMSYSERR_NOTSUPPORTED;
+
+    AcquireEntrypointMutex(MIDI_OUT_DEVICE_TYPE);
+
+    SND_TRACE(L"modMessage - Message type %d\n", Message);
+
+    switch ( Message )
+    {
+        case MODM_GETNUMDEVS :
+        {
+            Result = GetSoundDeviceCount(MIDI_OUT_DEVICE_TYPE);
+            break;
+        }
+
+        case MODM_GETDEVCAPS :
+        {
+            Result = MmeGetSoundDeviceCapabilities(MIDI_OUT_DEVICE_TYPE,
+                                                   DeviceId,
+                                                   (PVOID) Parameter1,
+                                                   Parameter2);
+            break;
+        }
+    }
+
+    SND_TRACE(L"modMessage returning MMRESULT %d\n", Result);
+
+    ReleaseEntrypointMutex(MIDI_OUT_DEVICE_TYPE);
+
+    return Result;
+}
diff --git a/reactos/lib/drivers/sound/mmebuddy/mixer/mxdMessage.c b/reactos/lib/drivers/sound/mmebuddy/mixer/mxdMessage.c
new file mode 100644 (file)
index 0000000..e9911c1
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * PROJECT:     ReactOS Sound System "MME Buddy" Library
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        lib/sound/mmebuddy/mixer/mxdMessage.c
+ *
+ * PURPOSE:     Provides the mxdMessage exported function, as required by
+ *              the MME API, for mixer device support.
+ *
+ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
+*/
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+
+#include <ntddsnd.h>
+
+#include <mmebuddy.h>
+
+/*
+    Standard MME driver entry-point for messages relating to mixers.
+*/
+APIENTRY DWORD
+mxdMessage(
+    DWORD DeviceId,
+    DWORD Message,
+    DWORD PrivateHandle,
+    DWORD Parameter1,
+    DWORD Parameter2)
+{
+    MMRESULT Result = MMSYSERR_NOTSUPPORTED;
+
+    AcquireEntrypointMutex(MIXER_DEVICE_TYPE);
+
+    SND_TRACE(L"mxdMessage - Message type %d\n", Message);
+
+    switch ( Message )
+    {
+        case MXDM_GETNUMDEVS :
+        {
+            Result = GetSoundDeviceCount(MIXER_DEVICE_TYPE);
+            break;
+        }
+
+        case MXDM_GETDEVCAPS :
+        {
+            Result = MmeGetSoundDeviceCapabilities(MIXER_DEVICE_TYPE,
+                                                   DeviceId,
+                                                   (PVOID) Parameter1,
+                                                   Parameter2);
+            break;
+        }
+    }
+
+    SND_TRACE(L"mxdMessage returning MMRESULT %d\n", Result);
+
+    ReleaseEntrypointMutex(MIXER_DEVICE_TYPE);
+
+    return Result;
+}
diff --git a/reactos/lib/drivers/sound/mmebuddy/mmebuddy.rbuild b/reactos/lib/drivers/sound/mmebuddy/mmebuddy.rbuild
new file mode 100644 (file)
index 0000000..fe19ac5
--- /dev/null
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!DOCTYPE module SYSTEM "../../../../tools/rbuild/project.dtd">
+<module name="mmebuddy" type="staticlibrary" allowwarnings="false" unicode="yes">
+       <include base="ReactOS">include/reactos/libs/sound</include>
+       <define name="DEBUG_NT4">1</define>
+       <file>capabilities.c</file>
+       <file>devicelist.c</file>
+       <file>deviceinstance.c</file>
+       <file>functiontable.c</file>
+    <file>mmewrap.c</file>
+       <file>reentrancy.c</file>
+       <file>utility.c</file>
+       <file>kernel.c</file>
+    <file>thread.c</file>
+       <directory name="wave">
+               <file>widMessage.c</file>
+               <file>wodMessage.c</file>
+               <file>format.c</file>
+        <file>header.c</file>
+       </directory>
+       <directory name="midi">
+               <file>midMessage.c</file>
+               <file>modMessage.c</file>
+       </directory>
+       <directory name="mixer">
+               <file>mxdMessage.c</file>
+       </directory>
+       <directory name="auxiliary">
+               <file>auxMessage.c</file>
+       </directory>
+</module>
diff --git a/reactos/lib/drivers/sound/mmebuddy/mmewrap.c b/reactos/lib/drivers/sound/mmebuddy/mmewrap.c
new file mode 100644 (file)
index 0000000..0c299e3
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * PROJECT:     ReactOS Sound System "MME Buddy" Library
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        lib/drivers/sound/mmebuddy/mmewrap.c
+ *
+ * PURPOSE:     Interface between MME functions and MME Buddy's own.
+ *
+ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
+*/
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+#include <ntddsnd.h>
+#include <mmebuddy.h>
+
+/*
+    Call the client application when something interesting happens (MME API
+    defines "interesting things" as device open, close, and buffer
+    completion.)
+*/
+VOID
+NotifyMmeClient(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
+    IN  DWORD Message,
+    IN  DWORD Parameter)
+{
+    SND_ASSERT( SoundDeviceInstance );
+
+    SND_TRACE(L"MME client callback - message %d, parameter %d\n",
+              (int) Message,
+              (int) Parameter);
+
+    if ( SoundDeviceInstance->WinMM.ClientCallback )
+    {
+        DriverCallback(SoundDeviceInstance->WinMM.ClientCallback,
+                       HIWORD(SoundDeviceInstance->WinMM.Flags),
+                       SoundDeviceInstance->WinMM.Handle,
+                       Message,
+                       SoundDeviceInstance->WinMM.ClientCallbackInstanceData,
+                       Parameter,
+                       0);
+    }
+}
+
+/*
+    This is a helper function to alleviate some of the repetition involved with
+    implementing the various MME message functions.
+*/
+MMRESULT
+MmeGetSoundDeviceCapabilities(
+    IN  MMDEVICE_TYPE DeviceType,
+    IN  DWORD DeviceId,
+    IN  PVOID Capabilities,
+    IN  DWORD CapabilitiesSize)
+{
+    PSOUND_DEVICE SoundDevice;
+    MMRESULT Result;
+
+    SND_TRACE(L"MME *_GETCAPS for device %d of type %d\n", DeviceId, DeviceType);
+
+    /* FIXME: Validate device type and ID */
+    VALIDATE_MMSYS_PARAMETER( Capabilities );
+    VALIDATE_MMSYS_PARAMETER( CapabilitiesSize > 0 );
+
+    /* Our parameter checks are done elsewhere */
+    Result = GetSoundDevice(DeviceType, DeviceId, &SoundDevice);
+
+    if ( ! MMSUCCESS(Result) )
+        return Result;
+
+    return GetSoundDeviceCapabilities(SoundDevice,
+                                      Capabilities,
+                                      CapabilitiesSize);
+}
+
+MMRESULT
+MmeOpenWaveDevice(
+    IN  MMDEVICE_TYPE DeviceType,
+    IN  DWORD DeviceId,
+    IN  LPWAVEOPENDESC OpenParameters,
+    IN  DWORD Flags,
+    OUT DWORD* PrivateHandle)
+{
+    MMRESULT Result;
+
+    PSOUND_DEVICE SoundDevice;
+    PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
+    LPWAVEFORMATEX Format;
+
+    SND_TRACE(L"Opening wave device (WIDM_OPEN / WODM_OPEN)");
+
+    VALIDATE_MMSYS_PARAMETER( IS_WAVE_DEVICE_TYPE(DeviceType) );    /* FIXME? wave in too? */
+    VALIDATE_MMSYS_PARAMETER( OpenParameters );
+
+    Format = OpenParameters->lpFormat;
+
+    Result = GetSoundDevice(DeviceType, DeviceId, &SoundDevice);
+    if ( ! MMSUCCESS(Result) )
+        return TranslateInternalMmResult(Result);
+
+    /* Does this device support the format? */
+    Result = QueryWaveDeviceFormatSupport(SoundDevice, Format, sizeof(WAVEFORMATEX));
+    if ( ! MMSUCCESS(Result) )
+    {
+        SND_ERR(L"Format not supported\n");
+        return TranslateInternalMmResult(Result);
+    }
+
+    /* If the caller just wanted to know if a format is supported, end here */
+    if ( Flags & WAVE_FORMAT_QUERY )
+        return MMSYSERR_NOERROR;
+
+    /* Check that winmm gave us a private handle to fill */
+    VALIDATE_MMSYS_PARAMETER( PrivateHandle );
+
+    /* Create a sound device instance and open the sound device */
+    Result = CreateSoundDeviceInstance(SoundDevice, &SoundDeviceInstance);
+    if ( ! MMSUCCESS(Result) )
+        return TranslateInternalMmResult(Result);
+
+    Result = SetWaveDeviceFormat(SoundDeviceInstance, Format, sizeof(WAVEFORMATEX));
+    if ( ! MMSUCCESS(Result) )
+    {
+        /* TODO: Destroy sound instance */
+        return TranslateInternalMmResult(Result);
+    }
+
+    /* Store the device instance pointer in the private handle - is DWORD safe here? */
+    *PrivateHandle = (DWORD) SoundDeviceInstance;
+
+    /* Store the additional information we were given - FIXME: Need flags! */
+    SetSoundDeviceInstanceMmeData(SoundDeviceInstance,
+                                  (HDRVR)OpenParameters->hWave,
+                                  OpenParameters->dwCallback,
+                                  OpenParameters->dwInstance,
+                                  Flags);
+
+    /* Let the application know the device is open */
+    ReleaseEntrypointMutex(DeviceType);
+    NotifyMmeClient(SoundDeviceInstance,
+                    DeviceType == WAVE_OUT_DEVICE_TYPE ? WOM_OPEN : WIM_OPEN,
+                    0);
+
+    AcquireEntrypointMutex(DeviceType);
+
+    SND_TRACE(L"Wave device now open\n");
+
+    return MMSYSERR_NOERROR;
+}
+
+MMRESULT
+MmeCloseDevice(
+    IN  DWORD PrivateHandle)
+{
+    MMRESULT Result;
+    PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
+    PSOUND_DEVICE SoundDevice;
+    MMDEVICE_TYPE DeviceType;
+
+    SND_TRACE(L"Closing wave device (WIDM_CLOSE / WODM_CLOSE)\n");
+
+    VALIDATE_MMSYS_PARAMETER( PrivateHandle );
+    SoundDeviceInstance = (PSOUND_DEVICE_INSTANCE) PrivateHandle;
+
+    Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
+    if ( ! MMSUCCESS(Result) )
+        return TranslateInternalMmResult(Result);
+
+    Result = GetSoundDeviceType(SoundDevice, &DeviceType);
+    if ( ! MMSUCCESS(Result) )
+        return TranslateInternalMmResult(Result);
+
+    ReleaseEntrypointMutex(DeviceType);
+    NotifyMmeClient(SoundDeviceInstance,
+                    DeviceType == WAVE_OUT_DEVICE_TYPE ? WOM_CLOSE : WIM_CLOSE,
+                    0);
+    AcquireEntrypointMutex(DeviceType);
+
+    Result = DestroySoundDeviceInstance(SoundDeviceInstance);
+
+    return Result;
+}
diff --git a/reactos/lib/drivers/sound/mmebuddy/reentrancy.c b/reactos/lib/drivers/sound/mmebuddy/reentrancy.c
new file mode 100644 (file)
index 0000000..8a93c74
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * PROJECT:     ReactOS Sound System "MME Buddy" Library
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        lib/sound/mmebuddy/reentrancy.c
+ *
+ * PURPOSE:     Provides entry-point mutex guards.
+ *
+ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
+*/
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+#include <ntddsnd.h>
+#include <mmebuddy.h>
+
+HANDLE EntrypointMutexes[SOUND_DEVICE_TYPES];
+
+/*
+    Creates a set of mutexes which are used for the purpose of guarding the
+    device-type specific module entry-points. If any of these fail creation,
+    all of them will be destroyed and the failure reported.
+*/
+MMRESULT
+InitEntrypointMutexes()
+{
+    UCHAR i;
+    MMRESULT Result = MMSYSERR_NOERROR;
+
+    /* Blank all entries ni the table first */
+    for ( i = 0; i < SOUND_DEVICE_TYPES; ++ i )
+    {
+        EntrypointMutexes[i] = NULL;
+    }
+
+    /* Now create the mutexes */
+    for ( i = 0; i < SOUND_DEVICE_TYPES; ++ i )
+    {
+        EntrypointMutexes[i] = CreateMutex(NULL, FALSE, NULL);
+
+        if ( ! EntrypointMutexes[i] )
+        {
+            Result = Win32ErrorToMmResult(GetLastError());
+
+            /* Clean up any mutexes we successfully created */
+            CleanupEntrypointMutexes();
+            break;
+        }
+    }
+
+    return Result;
+}
+
+/*
+    Cleans up any of the entry-point guard mutexes. This will only close the
+    handles of mutexes which have been created, making it safe for use as a
+    cleanup routine even within the InitEntrypointMutexes routine above.
+*/
+VOID
+CleanupEntrypointMutexes()
+{
+    UCHAR i;
+
+    /* Only clean up a mutex if it actually exists */
+    for ( i = 0; i < SOUND_DEVICE_TYPES; ++ i )
+    {
+        if ( EntrypointMutexes[i] )
+        {
+            CloseHandle(EntrypointMutexes[i]);
+            EntrypointMutexes[i] = NULL;
+        }
+    }
+}
+
+/*
+    Grabs an entry-point mutex.
+*/
+VOID
+AcquireEntrypointMutex(
+    IN  MMDEVICE_TYPE DeviceType)
+{
+    UCHAR i;
+
+    SND_ASSERT( IS_VALID_SOUND_DEVICE_TYPE(DeviceType) );
+    i = SOUND_DEVICE_TYPE_TO_INDEX(DeviceType);
+
+    SND_ASSERT( EntrypointMutexes[i] );
+
+    WaitForSingleObject(EntrypointMutexes[i], INFINITE);
+}
+
+/*
+    Releases an entry-point mutex.
+*/
+VOID
+ReleaseEntrypointMutex(
+    IN  MMDEVICE_TYPE DeviceType)
+{
+    UCHAR i;
+
+    SND_ASSERT( IS_VALID_SOUND_DEVICE_TYPE(DeviceType) );
+    i = SOUND_DEVICE_TYPE_TO_INDEX(DeviceType);
+
+    SND_ASSERT( EntrypointMutexes[i] );
+
+    ReleaseMutex(EntrypointMutexes[i]);
+}
diff --git a/reactos/lib/drivers/sound/mmebuddy/thread.c b/reactos/lib/drivers/sound/mmebuddy/thread.c
new file mode 100644 (file)
index 0000000..cc8ae7e
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * PROJECT:     ReactOS Sound System "MME Buddy" Library
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        lib/drivers/sound/mmebuddy/thread.c
+ *
+ * PURPOSE:     Multimedia thread management
+ *
+ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
+*/
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+#include <ntddsnd.h>
+#include <mmebuddy.h>
+
+DWORD WINAPI
+SoundThreadMain(
+    IN  LPVOID lpParameter OPTIONAL)
+{
+    PSOUND_THREAD Thread = (PSOUND_THREAD) lpParameter;
+
+    SND_TRACE(L"SoundThread running :)\n");
+
+    /* Callers will wait for us to be ready */
+    Thread->Running = TRUE;
+    SetEvent(Thread->Events.Ready);
+
+    while ( Thread->Running )
+    {
+        DWORD WaitResult;
+
+        /* Wait for a request, or an I/O completion */
+        WaitResult = WaitForSingleObjectEx(Thread->Events.Request, INFINITE, TRUE);
+        SND_TRACE(L"SoundThread - Came out of waiting\n");
+
+        if ( WaitResult == WAIT_OBJECT_0 )
+        {
+            SND_TRACE(L"SoundThread - Processing request\n");
+
+            if ( Thread->Request.Handler )
+            {
+                Thread->Request.Result = Thread->Request.Handler(Thread->Request.SoundDeviceInstance,
+                                                                 Thread->Request.Parameter);
+            }
+            else
+            {
+                Thread->Request.Result = MMSYSERR_ERROR;
+            }
+
+            /* Announce completion of the request */
+            SetEvent(Thread->Events.Done);
+            /* Accept new requests */
+            SetEvent(Thread->Events.Ready);
+        }
+        else if ( WaitResult == WAIT_IO_COMPLETION )
+        {
+            SND_TRACE(L"SoundThread - Processing IO completion\n");
+            /* TODO */
+        }
+        else
+        {
+            /* This should not happen! */
+            SND_ASSERT(FALSE);
+        }
+
+    }
+
+    return 0;
+}
+
+MMRESULT
+CreateSoundThreadEvents(
+    OUT HANDLE* ReadyEvent,
+    OUT HANDLE* RequestEvent,
+    OUT HANDLE* DoneEvent)
+{
+    BOOL ok;
+
+    VALIDATE_MMSYS_PARAMETER( ReadyEvent );
+    VALIDATE_MMSYS_PARAMETER( RequestEvent );
+    VALIDATE_MMSYS_PARAMETER( DoneEvent );
+
+    SND_TRACE(L"Creating thread events\n");
+
+    /* Initialise these so we can identify them upon failure */
+    *ReadyEvent = *RequestEvent = *DoneEvent = INVALID_HANDLE_VALUE;
+
+    ok = (*ReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) != INVALID_HANDLE_VALUE;
+    ok &= (*RequestEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) != INVALID_HANDLE_VALUE;
+    ok &= (*DoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) != INVALID_HANDLE_VALUE;
+
+    /* If something went wrong, clean up */
+    if ( ! ok )
+    {
+        if ( *ReadyEvent != INVALID_HANDLE_VALUE )
+            CloseHandle(*ReadyEvent);
+
+        if ( *RequestEvent != INVALID_HANDLE_VALUE )
+            CloseHandle(*RequestEvent);
+
+        if ( *DoneEvent != INVALID_HANDLE_VALUE )
+            CloseHandle(*DoneEvent);
+
+        return MMSYSERR_NOMEM;
+    }
+
+    return MMSYSERR_NOERROR;
+}
+
+MMRESULT
+DestroySoundThreadEvents(
+    IN  HANDLE ReadyEvent,
+    IN  HANDLE RequestEvent,
+    IN  HANDLE DoneEvent)
+{
+    VALIDATE_MMSYS_PARAMETER( ReadyEvent != INVALID_HANDLE_VALUE );
+    VALIDATE_MMSYS_PARAMETER( RequestEvent != INVALID_HANDLE_VALUE );
+    VALIDATE_MMSYS_PARAMETER( DoneEvent != INVALID_HANDLE_VALUE );
+
+    SND_TRACE(L"Destroying thread events\n");
+
+    CloseHandle(ReadyEvent);
+    CloseHandle(RequestEvent);
+    CloseHandle(DoneEvent);
+
+    return MMSYSERR_NOERROR;
+}
+
+MMRESULT
+CreateSoundThread(
+    OUT PSOUND_THREAD* Thread)
+{
+    MMRESULT Result;
+    PSOUND_THREAD NewThread;
+
+    VALIDATE_MMSYS_PARAMETER( Thread );
+
+    NewThread = AllocateStruct(SOUND_THREAD);
+    if ( ! NewThread )
+        return MMSYSERR_NOMEM;
+
+    /* Prepare the events we'll be using to sync. everything */
+    Result = CreateSoundThreadEvents(&NewThread->Events.Ready,
+                                     &NewThread->Events.Request,
+                                     &NewThread->Events.Done);
+
+    if ( ! MMSUCCESS(Result) )
+    {
+        FreeMemory(NewThread);
+        return TranslateInternalMmResult(Result);
+    }
+
+    SND_TRACE(L"Creating a sound thread\n");
+    NewThread->Handle = CreateThread(NULL,
+                                     0,
+                                     &SoundThreadMain,
+                                     (LPVOID) NewThread,
+                                     CREATE_SUSPENDED,
+                                     NULL);
+
+    /* Something went wrong, bail out! */
+    if ( NewThread->Handle == INVALID_HANDLE_VALUE )
+    {
+        SND_ERR(L"Sound thread creation failed!\n");
+        DestroySoundThreadEvents(NewThread->Events.Ready,
+                                 NewThread->Events.Request,
+                                 NewThread->Events.Done);
+
+        FreeMemory(NewThread);
+
+        return Win32ErrorToMmResult(GetLastError());
+    }
+
+    /* Wake the thread up */
+    if ( ResumeThread(NewThread->Handle) == -1 )
+    {
+        CloseHandle(NewThread->Handle);
+        DestroySoundThreadEvents(NewThread->Events.Ready,
+                                 NewThread->Events.Request,
+                                 NewThread->Events.Done);
+
+        FreeMemory(NewThread);
+        return Win32ErrorToMmResult(GetLastError());
+    }
+
+    /* If all is well we can now give the thread to the caller */
+    *Thread = NewThread;
+    return MMSYSERR_NOERROR;
+}
+
+MMRESULT
+DestroySoundThread(
+    IN  PSOUND_THREAD Thread)
+{
+    /* TODO: Implement me! */
+    return MMSYSERR_NOTSUPPORTED;
+}
+
+MMRESULT
+CallSoundThread(
+    IN  PSOUND_THREAD Thread,
+    IN  SOUND_THREAD_REQUEST_HANDLER RequestHandler,
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance OPTIONAL,
+    IN  PVOID Parameter OPTIONAL)
+{
+    VALIDATE_MMSYS_PARAMETER( Thread );
+    VALIDATE_MMSYS_PARAMETER( RequestHandler );
+
+    SND_TRACE(L"Waiting for READY event\n");
+    WaitForSingleObject(Thread->Events.Ready, INFINITE);
+
+    Thread->Request.Result = MMSYSERR_NOTSUPPORTED;
+    Thread->Request.Handler = RequestHandler;
+    Thread->Request.SoundDeviceInstance = SoundDeviceInstance;
+    Thread->Request.Parameter = Parameter;
+
+    /* Notify the thread it has work to do */
+    SND_TRACE(L"Setting REQUEST event\n");
+    SetEvent(Thread->Events.Request);
+
+    /* Wait for the work to be done */
+    SND_TRACE(L"Waiting for DONE event\n");
+    WaitForSingleObject(Thread->Events.Done, INFINITE);
+
+    return Thread->Request.Result;
+}
diff --git a/reactos/lib/drivers/sound/mmebuddy/utility.c b/reactos/lib/drivers/sound/mmebuddy/utility.c
new file mode 100644 (file)
index 0000000..db54799
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * PROJECT:     ReactOS Sound System "MME Buddy" Library
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        lib/sound/mmebuddy/utility.c
+ *
+ * PURPOSE:     Provides utility functions used by the library.
+ *
+ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
+*/
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+
+#include <mmebuddy.h>
+
+static HANDLE ProcessHeapHandle = NULL;
+static UINT   CurrentAllocations = 0;
+
+/*
+    Allocates memory, zeroes it, and increases the allocation count.
+*/
+PVOID
+AllocateMemory(
+    IN  UINT Size)
+{
+    PVOID Pointer = NULL;
+
+    if ( ! ProcessHeapHandle )
+        ProcessHeapHandle = GetProcessHeap();
+
+    Pointer = HeapAlloc(ProcessHeapHandle, HEAP_ZERO_MEMORY, Size);
+
+    if ( ! Pointer )
+        return NULL;
+
+    ++ CurrentAllocations;
+
+    return Pointer;
+}
+
+/*
+    Frees memory and reduces the allocation count.
+*/
+VOID
+FreeMemory(
+    IN  PVOID Pointer)
+{
+    SND_ASSERT( ProcessHeapHandle );
+    SND_ASSERT( Pointer );
+
+    HeapFree(ProcessHeapHandle, 0, Pointer);
+
+    -- CurrentAllocations;
+}
+
+/*
+    Returns the current number of memory allocations outstanding. Useful for
+    detecting/tracing memory leaks.
+*/
+UINT
+GetMemoryAllocationCount()
+{
+    return CurrentAllocations;
+}
+
+
+/*
+    Count the number of digits in a UINT
+*/
+UINT
+GetDigitCount(
+    IN  UINT Number)
+{
+    UINT Value = Number;
+    ULONG Digits = 1;
+
+    while ( Value > 9 )
+    {
+        Value /= 10;
+        ++ Digits;
+    }
+
+    return Digits;
+}
+
+/*
+    Translate a Win32 error code into an MMRESULT code.
+*/
+MMRESULT
+Win32ErrorToMmResult(
+    IN  UINT ErrorCode)
+{
+    switch ( ErrorCode )
+    {
+        case NO_ERROR :
+        case ERROR_IO_PENDING :
+            return MMSYSERR_NOERROR;
+
+        case ERROR_BUSY :
+            return MMSYSERR_ALLOCATED;
+
+        case ERROR_NOT_SUPPORTED :
+        case ERROR_INVALID_FUNCTION :
+            return MMSYSERR_NOTSUPPORTED;
+
+        case ERROR_NOT_ENOUGH_MEMORY :
+            return MMSYSERR_NOMEM;
+
+        case ERROR_ACCESS_DENIED :
+            return MMSYSERR_BADDEVICEID;
+
+        case ERROR_INSUFFICIENT_BUFFER :
+            return MMSYSERR_INVALPARAM;
+
+        default :
+            return MMSYSERR_ERROR;
+    }
+}
+
+/*
+    If a function invokes another function, this aids in translating the
+    result code so that it is applicable in the context of the original caller.
+    For example, specifying that an invalid parameter was passed probably does
+    not make much sense if the parameter wasn't passed by the original caller!
+
+    This could potentially highlight internal logic problems.
+
+    However, things like MMSYSERR_NOMEM make sense to return to the caller.
+*/
+MMRESULT
+TranslateInternalMmResult(
+    IN  MMRESULT Result)
+{
+    switch ( Result )
+    {
+        case MMSYSERR_INVALPARAM :
+        case MMSYSERR_INVALFLAG :
+        {
+            return MMSYSERR_ERROR;
+        }
+    }
+
+    return Result;
+}
diff --git a/reactos/lib/drivers/sound/mmebuddy/wave/format.c b/reactos/lib/drivers/sound/mmebuddy/wave/format.c
new file mode 100644 (file)
index 0000000..d4f64b5
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * PROJECT:     ReactOS Sound System "MME Buddy" Library
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        lib/drivers/sound/mmebuddy/wave/format.c
+ *
+ * PURPOSE:     Queries and sets wave device format (sample rate, etc.)
+ *
+ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
+*/
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+#include <ntddsnd.h>
+#include <mmebuddy.h>
+
+MMRESULT
+QueryWaveDeviceFormatSupport(
+    IN  PSOUND_DEVICE SoundDevice,
+    IN  LPWAVEFORMATEX Format,
+    IN  DWORD FormatSize)
+{
+    MMRESULT Result;
+    MMDEVICE_TYPE DeviceType;
+    PMMFUNCTION_TABLE FunctionTable;
+
+    SND_TRACE(L"Querying wave format support\n");
+
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDevice(SoundDevice) );
+    VALIDATE_MMSYS_PARAMETER( Format );
+    VALIDATE_MMSYS_PARAMETER( FormatSize >= sizeof(WAVEFORMATEX) );
+
+    Result = GetSoundDeviceType(SoundDevice, &DeviceType);
+    SND_ASSERT( Result == MMSYSERR_NOERROR );
+
+    /* Ensure we have a wave device (TODO: check if this applies to wavein as well) */
+    VALIDATE_MMSYS_PARAMETER( IS_WAVE_DEVICE_TYPE(DeviceType) );
+
+    /* Obtain the function table */
+    Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
+    SND_ASSERT( Result == MMSYSERR_NOERROR );
+
+    if ( ! MMSUCCESS(Result) )
+        return TranslateInternalMmResult(Result);
+
+    if ( ! FunctionTable->QueryWaveFormatSupport )
+        return MMSYSERR_NOTSUPPORTED;
+
+    return FunctionTable->QueryWaveFormatSupport(SoundDevice, Format, FormatSize);
+}
+
+MMRESULT
+SetWaveDeviceFormat(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
+    IN  LPWAVEFORMATEX Format,
+    IN  DWORD FormatSize)
+{
+    MMRESULT Result;
+    MMDEVICE_TYPE DeviceType;
+    PMMFUNCTION_TABLE FunctionTable;
+    PSOUND_DEVICE SoundDevice;
+
+    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) )
+        return TranslateInternalMmResult(Result);
+
+    Result = GetSoundDeviceType(SoundDevice, &DeviceType);
+    SND_ASSERT( Result == MMSYSERR_NOERROR );
+
+    /* Ensure we have a wave device (TODO: check if this applies to wavein as well) */
+    VALIDATE_MMSYS_PARAMETER( IS_WAVE_DEVICE_TYPE(DeviceType) );
+
+    /* Obtain the function table */
+    Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
+    SND_ASSERT( Result == MMSYSERR_NOERROR );
+
+    if ( ! MMSUCCESS(Result) )
+        return TranslateInternalMmResult(Result);
+
+    if ( ! FunctionTable->SetWaveFormat )
+        return MMSYSERR_NOTSUPPORTED;
+
+    return FunctionTable->SetWaveFormat(SoundDeviceInstance, Format, FormatSize);
+}
diff --git a/reactos/lib/drivers/sound/mmebuddy/wave/header.c b/reactos/lib/drivers/sound/mmebuddy/wave/header.c
new file mode 100644 (file)
index 0000000..dbaad8e
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * PROJECT:     ReactOS Sound System "MME Buddy" Library
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        lib/drivers/sound/mmebuddy/header.c
+ *
+ * PURPOSE:     Wave header preparation routines
+ *
+ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
+*/
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+#include <ntddsnd.h>
+#include <mmebuddy.h>
+
+MMRESULT
+PrepareWaveHeader(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
+    IN  PWAVEHDR Header)
+{
+    MMRESULT Result;
+    PSOUND_DEVICE SoundDevice;
+    PMMFUNCTION_TABLE FunctionTable;
+
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
+    VALIDATE_MMSYS_PARAMETER( Header );
+
+    SND_TRACE(L"Preparing wave header\n");
+
+    Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
+    if ( ! MMSUCCESS(Result) )
+        return TranslateInternalMmResult(Result);
+
+    Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
+    if ( ! MMSUCCESS(Result) )
+        return TranslateInternalMmResult(Result);
+
+    if ( ! FunctionTable->PrepareWaveHeader )
+        return MMSYSERR_NOTSUPPORTED;
+
+    return FunctionTable->PrepareWaveHeader(SoundDeviceInstance, Header);
+}
+
+MMRESULT
+UnprepareWaveHeader(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
+    IN  PWAVEHDR Header)
+{
+    MMRESULT Result;
+    PSOUND_DEVICE SoundDevice;
+    PMMFUNCTION_TABLE FunctionTable;
+
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
+    VALIDATE_MMSYS_PARAMETER( Header );
+
+    SND_TRACE(L"Un-preparing wave header\n");
+
+    Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
+    if ( ! MMSUCCESS(Result) )
+        return TranslateInternalMmResult(Result);
+
+    Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
+    if ( ! MMSUCCESS(Result) )
+        return TranslateInternalMmResult(Result);
+
+    if ( ! FunctionTable->UnprepareWaveHeader )
+        return MMSYSERR_NOTSUPPORTED;
+
+    return FunctionTable->UnprepareWaveHeader(SoundDeviceInstance, Header);
+}
+
+MMRESULT
+SubmitWaveHeader(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
+    IN  PWAVEHDR Header)
+{
+    MMRESULT Result;
+    PSOUND_DEVICE SoundDevice;
+    PMMFUNCTION_TABLE FunctionTable;
+
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
+    VALIDATE_MMSYS_PARAMETER( Header );
+
+    SND_TRACE(L"Submitting wave header\n");
+
+    Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
+    if ( ! MMSUCCESS(Result) )
+        return TranslateInternalMmResult(Result);
+
+    Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
+    if ( ! MMSUCCESS(Result) )
+        return TranslateInternalMmResult(Result);
+
+    if ( ! FunctionTable->SubmitWaveHeader )
+        return MMSYSERR_NOTSUPPORTED;
+
+    return FunctionTable->SubmitWaveHeader(SoundDeviceInstance, Header);
+}
+
diff --git a/reactos/lib/drivers/sound/mmebuddy/wave/widMessage.c b/reactos/lib/drivers/sound/mmebuddy/wave/widMessage.c
new file mode 100644 (file)
index 0000000..9fdbbda
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * PROJECT:     ReactOS Sound System "MME Buddy" Library
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        lib/sound/mmebuddy/wave/widMessage.c
+ *
+ * PURPOSE:     Provides the widMessage exported function, as required by
+ *              the MME API, for wave input device support.
+ *
+ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
+*/
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+
+#include <ntddsnd.h>
+
+#include <mmebuddy.h>
+
+/*
+    Standard MME driver entry-point for messages relating to wave audio
+    input.
+*/
+APIENTRY DWORD
+widMessage(
+    DWORD DeviceId,
+    DWORD Message,
+    DWORD PrivateHandle,
+    DWORD Parameter1,
+    DWORD Parameter2)
+{
+    MMRESULT Result = MMSYSERR_NOTSUPPORTED;
+
+    AcquireEntrypointMutex(WAVE_IN_DEVICE_TYPE);
+
+    SND_TRACE(L"widMessage - Message type %d\n", Message);
+
+    switch ( Message )
+    {
+        case WIDM_GETNUMDEVS :
+        {
+            Result = GetSoundDeviceCount(WAVE_IN_DEVICE_TYPE);
+            break;
+        }
+
+        case WIDM_GETDEVCAPS :
+        {
+            Result = MmeGetSoundDeviceCapabilities(WAVE_IN_DEVICE_TYPE,
+                                                   DeviceId,
+                                                   (PVOID) Parameter1,
+                                                   Parameter2);
+            break;
+        }
+    }
+
+    SND_TRACE(L"widMessage returning MMRESULT %d\n", Result);
+
+    ReleaseEntrypointMutex(WAVE_IN_DEVICE_TYPE);
+
+    return Result;
+}
diff --git a/reactos/lib/drivers/sound/mmebuddy/wave/wodMessage.c b/reactos/lib/drivers/sound/mmebuddy/wave/wodMessage.c
new file mode 100644 (file)
index 0000000..27dcbb9
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * PROJECT:     ReactOS Sound System "MME Buddy" Library
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        lib/drivers/sound/mmebuddy/wave/wodMessage.c
+ *
+ * PURPOSE:     Provides the wodMessage exported function, as required by
+ *              the MME API, for wave output device support.
+ *
+ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
+*/
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+
+#include <ntddsnd.h>
+
+#include <mmebuddy.h>
+
+#if 0
+MMRESULT HelloWorld(PSOUND_DEVICE_INSTANCE Instance, PVOID String)
+{
+    PWSTR WString = (PWSTR) String;
+    SND_TRACE(WString);
+    return MMSYSERR_NOTSUPPORTED;
+}
+#endif
+
+/*
+    Standard MME driver entry-point for messages relating to wave audio
+    output.
+*/
+APIENTRY DWORD
+wodMessage(
+    DWORD DeviceId,
+    DWORD Message,
+    DWORD PrivateHandle,
+    DWORD Parameter1,
+    DWORD Parameter2)
+{
+    MMRESULT Result = MMSYSERR_NOTSUPPORTED;
+
+    AcquireEntrypointMutex(WAVE_OUT_DEVICE_TYPE);
+
+    SND_TRACE(L"wodMessage - Message type %d\n", Message);
+
+    switch ( Message )
+    {
+        case WODM_GETNUMDEVS :
+        {
+            Result = GetSoundDeviceCount(WAVE_OUT_DEVICE_TYPE);
+            break;
+        }
+
+        case WODM_GETDEVCAPS :
+        {
+            Result = MmeGetSoundDeviceCapabilities(WAVE_OUT_DEVICE_TYPE,
+                                                   DeviceId,
+                                                   (PVOID) Parameter1,
+                                                   Parameter2);
+            break;
+        }
+
+        case WODM_OPEN :
+        {
+            Result = MmeOpenWaveDevice(WAVE_OUT_DEVICE_TYPE,
+                                       DeviceId,
+                                       (LPWAVEOPENDESC) Parameter1,
+                                       Parameter2,
+                                       (DWORD*) PrivateHandle);
+            break;
+        }
+
+        case WODM_CLOSE :
+        {
+            Result = MmeCloseDevice(PrivateHandle);
+
+            break;
+        }
+
+        case WODM_PREPARE :
+        {
+            /* TODO: Do we need to pass 2nd parameter? */
+            Result = MmePrepareWaveHeader(PrivateHandle, Parameter1);
+            break;
+        }
+
+        case WODM_UNPREPARE :
+        {
+            Result = MmeUnprepareWaveHeader(PrivateHandle, Parameter1);
+            break;
+        }
+
+        case WODM_WRITE :
+        {
+            Result = MmeSubmitWaveHeader(PrivateHandle, Parameter1);
+            break;
+        }
+
+        case WODM_GETPOS :
+        {
+#if 0
+            /* Hacky code to test the threading */
+            PSOUND_DEVICE_INSTANCE Instance = (PSOUND_DEVICE_INSTANCE)PrivateHandle;
+            CallSoundThread(Instance->Thread, HelloWorld, Instance, L"Hello World!");
+            CallSoundThread(Instance->Thread, HelloWorld, Instance, L"Hello Universe!");
+#endif
+            break;
+        }
+    }
+
+    SND_TRACE(L"wodMessage returning MMRESULT %d\n", Result);
+
+    ReleaseEntrypointMutex(WAVE_OUT_DEVICE_TYPE);
+
+    return Result;
+}
diff --git a/reactos/lib/drivers/sound/mment4/control.c b/reactos/lib/drivers/sound/mment4/control.c
new file mode 100644 (file)
index 0000000..27c7ed4
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * PROJECT:     ReactOS Sound System "MME Buddy" NT4 Library
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        lib/drivers/sound/mment4/control.c
+ *
+ * PURPOSE:     Device control for NT4 audio devices
+ *
+ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
+*/
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+#include <ntddsnd.h>
+#include <mmebuddy.h>
+#include <mment4.h>
+
+/*
+    Convenience routine for getting the path of a device and opening it.
+*/
+MMRESULT
+OpenNt4KernelSoundDevice(
+    IN  PSOUND_DEVICE SoundDevice,
+    IN  BOOLEAN ReadOnly,
+    OUT PHANDLE Handle)
+{
+    PWSTR Path;
+    MMRESULT Result;
+
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDevice(SoundDevice) );
+    VALIDATE_MMSYS_PARAMETER( Handle );
+
+    Result = GetSoundDeviceIdentifier(SoundDevice, (PVOID*) &Path);
+    if ( ! MMSUCCESS(Result) )
+    {
+        SND_ERR(L"Unable to get sound device path");
+        return TranslateInternalMmResult(Result);
+    }
+
+    SND_ASSERT( Path );
+
+    return OpenKernelSoundDeviceByName(Path, ReadOnly, Handle);
+}
+
+/*
+    Device open/close. These are basically wrappers for the MME-Buddy
+    open and close routines, which provide a Windows device handle.
+    These may seem simple but as you can return pretty much anything
+    as the handle, we could just as easily return a structure etc.
+*/
+MMRESULT
+OpenNt4SoundDevice(
+    IN  PSOUND_DEVICE SoundDevice,
+    OUT PVOID* Handle)
+{
+    SND_TRACE(L"Opening NT4 style sound device\n");
+
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDevice(SoundDevice) );
+    VALIDATE_MMSYS_PARAMETER( Handle );
+
+    return OpenNt4KernelSoundDevice(SoundDevice, FALSE, Handle);
+}
+
+MMRESULT
+CloseNt4SoundDevice(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
+    IN  PVOID Handle)
+{
+    SND_TRACE(L"Closing NT4 style sound device\n");
+
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
+    return CloseKernelSoundDevice((HANDLE) Handle);
+}
+
+/*
+    Provides an implementation for the "get capabilities" request,
+    using the standard IOCTLs used by NT4 sound drivers.
+*/
+MMRESULT
+GetNt4SoundDeviceCapabilities(
+    IN  PSOUND_DEVICE SoundDevice,
+    OUT PVOID Capabilities,
+    IN  DWORD CapabilitiesSize)
+{
+    MMRESULT Result;
+    MMDEVICE_TYPE DeviceType;
+    DWORD IoCtl;
+    HANDLE DeviceHandle;
+
+    /* If these are bad there's an internal error with MME-Buddy! */
+    SND_ASSERT( SoundDevice );
+    SND_ASSERT( Capabilities );
+    SND_ASSERT( CapabilitiesSize > 0 );
+
+    SND_TRACE(L"NT4 get-capabilities routine called\n");
+
+    /* Get the device type */
+    Result = GetSoundDeviceType(SoundDevice, &DeviceType);
+    SND_ASSERT( Result == MMSYSERR_NOERROR );
+
+    if ( ! MMSUCCESS(Result) );
+        return TranslateInternalMmResult(Result);
+
+    /* Choose the appropriate IOCTL */
+    if ( IS_WAVE_DEVICE_TYPE(DeviceType) )
+    {
+        IoCtl = IOCTL_WAVE_GET_CAPABILITIES;
+    }
+    else if ( IS_MIDI_DEVICE_TYPE(DeviceType) )
+    {
+        IoCtl = IOCTL_MIDI_GET_CAPABILITIES;
+    }
+    else
+    {
+        /* FIXME - need to support AUX and mixer devices */
+        SND_ASSERT( FALSE );
+    }
+
+    /* Get the capabilities information from the driver */
+    Result = OpenNt4KernelSoundDevice(SoundDevice, TRUE, &DeviceHandle);
+
+    if ( ! MMSUCCESS(Result) )
+    {
+        SND_ERR(L"Failed to open device");
+        return TranslateInternalMmResult(Result);
+    }
+
+    Result = SyncOverlappedDeviceIoControl(DeviceHandle,
+                                           IoCtl,
+                                           Capabilities,
+                                           CapabilitiesSize,
+                                           NULL,
+                                           0,
+                                           NULL);
+
+    CloseKernelSoundDevice(DeviceHandle);
+
+    if ( ! MMSUCCESS(Result) )
+    {
+        SND_ERR(L"Retrieval of capabilities information failed\n");
+        Result = TranslateInternalMmResult(Result);
+    }
+
+    return Result;
+}
+
+/*
+    Querying/setting the format of a wave device. Querying format support
+    requires us to first open the device, whereas setting format is done
+    on an already opened device.
+*/
+MMRESULT
+QueryNt4WaveDeviceFormatSupport(
+    IN  PSOUND_DEVICE SoundDevice,
+    IN  LPWAVEFORMATEX Format,
+    IN  DWORD FormatSize)
+{
+    MMRESULT Result;
+    HANDLE Handle;
+
+    SND_TRACE(L"NT4 wave format support querying routine called\n");
+
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDevice(SoundDevice) );
+    VALIDATE_MMSYS_PARAMETER( Format );
+    VALIDATE_MMSYS_PARAMETER( FormatSize >= sizeof(WAVEFORMATEX) );
+
+    /* Get the device path */
+    Result = OpenNt4KernelSoundDevice(SoundDevice,
+                                      FALSE,
+                                      &Handle);
+
+    if ( ! MMSUCCESS(Result) )
+    {
+        SND_ERR(L"Unable to open kernel sound device\n");
+        return TranslateInternalMmResult(Result);
+    }
+
+    Result = SyncOverlappedDeviceIoControl(Handle,
+                                           IOCTL_WAVE_QUERY_FORMAT,
+                                           (LPVOID) Format,
+                                           FormatSize,
+                                           NULL,
+                                           0,
+                                           NULL);
+
+    if ( ! MMSUCCESS(Result) )
+    {
+        SND_ERR(L"Sync overlapped I/O failed - MMSYS_ERROR %d\n", Result);
+        Result = TranslateInternalMmResult(Result);
+    }
+
+    CloseKernelSoundDevice(Handle);
+
+    return MMSYSERR_NOERROR;
+}
+
+MMRESULT
+SetNt4WaveDeviceFormat(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
+    IN  LPWAVEFORMATEX Format,
+    IN  DWORD FormatSize)
+{
+    MMRESULT Result;
+    HANDLE Handle;
+
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
+    VALIDATE_MMSYS_PARAMETER( Format );
+    VALIDATE_MMSYS_PARAMETER( FormatSize >= sizeof(WAVEFORMATEX) );
+
+    Result = GetSoundDeviceInstanceHandle(SoundDeviceInstance, &Handle);
+
+    if ( ! MMSUCCESS(Result) )
+        return TranslateInternalMmResult(Result);
+
+    SND_TRACE(L"Setting wave device format on handle %x\n", Handle);
+
+    Result = SyncOverlappedDeviceIoControl(Handle,
+                                           IOCTL_WAVE_SET_FORMAT,
+                                           (LPVOID) Format,
+                                           FormatSize,
+                                           NULL,
+                                           0,
+                                           NULL);
+
+    if ( ! MMSUCCESS(Result) )
+        return TranslateInternalMmResult(Result);
+
+    return MMSYSERR_NOERROR;
+}
+
diff --git a/reactos/lib/drivers/sound/mment4/detect.c b/reactos/lib/drivers/sound/mment4/detect.c
new file mode 100644 (file)
index 0000000..35e7af0
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * PROJECT:     ReactOS Sound System "MME Buddy" NT4 Library
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        lib/drivers/sound/mment4/detect.c
+ *
+ * PURPOSE:     Assists in locating Windows NT4 compatible sound devices,
+ *              which mostly use the same device naming convention and/or
+ *              store their created device names within their service key
+ *              within the registry.
+ *
+ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
+*/
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+#include <ntddsnd.h>
+
+#include <mmebuddy.h>
+#include <mment4.h>
+
+/*
+    This is the "nice" way to discover audio devices in NT4 - go into the
+    service registry key and enumerate the Parameters\Device*\Devices
+    values. The value names represent the device name, whereas the data
+    assigned to them identifies the type of device.
+*/
+MMRESULT
+EnumerateNt4ServiceSoundDevices(
+    IN  LPWSTR ServiceName,
+    IN  MMDEVICE_TYPE DeviceType,
+    IN  SOUND_DEVICE_DETECTED_PROC SoundDeviceDetectedProc)
+{
+    HKEY Key;
+    DWORD KeyIndex = 0;
+
+    VALIDATE_MMSYS_PARAMETER( ServiceName );
+
+    /* Device type zero means "all" */
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceType(DeviceType) ||
+                              DeviceType == 0 );
+
+    while ( OpenSoundDeviceRegKey(ServiceName, KeyIndex, &Key) == MMSYSERR_NOERROR )
+    {
+        HKEY DevicesKey;
+        DWORD ValueType = REG_NONE, ValueIndex = 0;
+        DWORD MaxNameLength = 0, ValueNameLength = 0;
+        PWSTR DevicePath = NULL, ValueName = NULL;
+        DWORD ValueDataLength = sizeof(DWORD);
+        DWORD ValueData;
+
+        if ( RegOpenKeyEx(Key,
+                          REG_DEVICES_KEY_NAME_U,
+                          0,
+                          KEY_READ,
+                          &DevicesKey) == ERROR_SUCCESS )
+        {
+            /* Find out how much memory is needed for the key name */
+            if ( RegQueryInfoKey(DevicesKey,
+                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                                 &MaxNameLength,
+                                 NULL, NULL, NULL) != ERROR_SUCCESS )
+            {
+                SND_ERR(L"Failed to query registry key information\n");
+                RegCloseKey(DevicesKey);
+                RegCloseKey(Key);
+
+                return MMSYSERR_ERROR;
+            }
+
+            DevicePath = AllocateWideString(MaxNameLength +
+                                            strlen("\\\\.\\"));
+
+            /* Check that the memory allocation was successful */
+            if ( ! DevicePath )
+            {
+                /* There's no point in going further */
+                RegCloseKey(DevicesKey);
+                RegCloseKey(Key);
+
+                return MMSYSERR_NOMEM;
+            }
+
+            /* Insert the device path prefix */
+            wsprintf(DevicePath, L"\\\\.\\");
+
+            /* The offset of the string following this prefix */
+            ValueName = DevicePath + strlen("\\\\.\\");
+
+            /* Copy this so that it may be overwritten - include NULL */
+            ValueNameLength = MaxNameLength + sizeof(WCHAR);
+
+            SND_TRACE(L"Interested in devices beginning with %wS\n", DevicePath);
+
+            while ( RegEnumValue(DevicesKey,
+                                 ValueIndex,
+                                 ValueName,
+                                 &ValueNameLength,
+                                 NULL,
+                                 &ValueType,
+                                 (LPBYTE) &ValueData,
+                                 &ValueDataLength) == ERROR_SUCCESS )
+            {
+                /* Device types are stored as DWORDs */
+                if ( ( ValueType == REG_DWORD ) &&
+                     ( ValueDataLength == sizeof(DWORD) ) )
+                {
+                    if ( ( DeviceType == 0 ) ||
+                         ( DeviceType == ValueData ) )
+                    {
+                        SND_TRACE(L"Found device: %wS\n", DevicePath);
+                        SoundDeviceDetectedProc(ValueData, DevicePath);
+                    }
+                }
+
+                /* Reset variables for the next iteration */
+                ValueNameLength = MaxNameLength + sizeof(WCHAR);
+                ZeroMemory(ValueName, (MaxNameLength+1)*sizeof(WCHAR));
+                /*ZeroWideString(ValueName);*/
+                ValueDataLength = sizeof(DWORD);
+                ValueData = 0;
+                ValueType = REG_NONE;
+
+                ++ ValueIndex;
+            }
+
+            FreeMemory(DevicePath);
+
+            RegCloseKey(DevicesKey);
+        }
+        else
+        {
+            SND_WARN(L"Unable to open the Devices key!\n");
+        }
+
+        ++ KeyIndex;
+
+        RegCloseKey(Key);
+    }
+
+    return MMSYSERR_NOERROR;
+}
+
+/*
+    Brute-force device detection, using a base device name (eg: \\.\WaveOut).
+
+    This will add the device number as a suffix to the end of the string and
+    attempt to open the device based on that name. On success, it will
+    increment the device number and repeat this process.
+
+    When it runs out of devices, it will give up.
+*/
+MMRESULT
+DetectNt4SoundDevices(
+    IN  MMDEVICE_TYPE DeviceType,
+    IN  PWSTR BaseDeviceName,
+    IN  SOUND_DEVICE_DETECTED_PROC SoundDeviceDetectedProc)
+{
+    ULONG DeviceNameLength = 0;
+    PWSTR DeviceName = NULL;
+    ULONG Index = 0;
+    HANDLE DeviceHandle;
+    BOOLEAN DoSearch = TRUE;
+
+    VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceType(DeviceType) );
+
+    DeviceNameLength = wcslen(BaseDeviceName);
+    /* Consider the length of the number */
+    DeviceNameLength += GetDigitCount(Index);
+
+    DeviceName = AllocateWideString(DeviceNameLength);
+
+    if ( ! DeviceName )
+    {
+        return MMSYSERR_NOMEM;
+    }
+
+    while ( DoSearch )
+    {
+        /* Nothing like a nice clean device name */
+        ZeroWideString(DeviceName);
+        wsprintf(DeviceName, L"%ls%d", BaseDeviceName, Index);
+
+        if ( OpenKernelSoundDeviceByName(DeviceName,
+                                         TRUE,
+                                         &DeviceHandle) == MMSYSERR_NOERROR )
+        {
+            /* Notify the callback function */
+            SND_TRACE(L"Found device: %wS\n", DeviceName);
+            SoundDeviceDetectedProc(DeviceType, DeviceName);
+
+            CloseHandle(DeviceHandle);
+
+            ++ Index;
+        }
+        else
+        {
+            DoSearch = FALSE;
+        }
+    }
+
+    FreeMemory(DeviceName);
+
+    return MMSYSERR_NOERROR;
+}
diff --git a/reactos/lib/drivers/sound/mment4/mment4.rbuild b/reactos/lib/drivers/sound/mment4/mment4.rbuild
new file mode 100644 (file)
index 0000000..dbd777e
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<!DOCTYPE module SYSTEM "../../../../tools/rbuild/project.dtd">
+<module name="mment4" type="staticlibrary" allowwarnings="false" unicode="yes">
+       <include base="ReactOS">include/reactos/libs/sound</include>
+       <define name="DEBUG_NT4">1</define>
+       <file>detect.c</file>
+       <file>registry.c</file>
+    <file>control.c</file>
+</module>
diff --git a/reactos/lib/drivers/sound/mment4/registry.c b/reactos/lib/drivers/sound/mment4/registry.c
new file mode 100644 (file)
index 0000000..3cffee1
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * PROJECT:     ReactOS Sound System "MME Buddy" NT4 Library
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        lib/drivers/sound/mment4/registry.c
+ *
+ * PURPOSE:     Registry operation helper for audio device drivers.
+ *
+ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
+*/
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+#include <ntddsnd.h>
+
+#include <mmebuddy.h>
+#include <mment4.h>
+
+/*
+    Open the parameters key of a sound driver.
+    NT4 only.
+*/
+MMRESULT
+OpenSoundDriverParametersRegKey(
+    IN  LPWSTR ServiceName,
+    OUT PHKEY KeyHandle)
+{
+    ULONG KeyLength;
+    PWCHAR ParametersKeyName;
+
+    VALIDATE_MMSYS_PARAMETER( ServiceName );
+    VALIDATE_MMSYS_PARAMETER( KeyHandle );
+
+    /* Work out how long the string will be */
+    KeyLength = wcslen(REG_SERVICES_KEY_NAME_U) + 1
+              + wcslen(ServiceName) + 1
+              + wcslen(REG_PARAMETERS_KEY_NAME_U);
+
+    /* Allocate memory for the string */
+    ParametersKeyName = AllocateWideString(KeyLength);
+
+    if ( ! ParametersKeyName )
+        return MMSYSERR_NOMEM;
+
+    /* Construct the registry path */
+    wsprintf(ParametersKeyName,
+             L"%s\\%s\\%s",
+             REG_SERVICES_KEY_NAME_U,
+             ServiceName,
+             REG_PARAMETERS_KEY_NAME_U);
+
+    SND_TRACE(L"Opening reg key: %wS\n", ParametersKeyName);
+
+    /* Perform the open */
+    if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+                      ParametersKeyName,
+                      0,
+                      KEY_READ,
+                      KeyHandle) != ERROR_SUCCESS )
+    {
+        /* Couldn't open the key */
+        SND_ERR(L"Failed to open reg key: %wS\n", ParametersKeyName);
+        FreeMemory(ParametersKeyName);
+        return MMSYSERR_ERROR;
+    }
+
+    FreeMemory(ParametersKeyName);
+
+    return MMSYSERR_NOERROR;
+}
+
+/*
+    Open one of the Device sub-keys belonging to the sound driver.
+    NT4 only.
+*/
+MMRESULT
+OpenSoundDeviceRegKey(
+    IN  LPWSTR ServiceName,
+    IN  DWORD DeviceIndex,
+    OUT PHKEY KeyHandle)
+{
+    DWORD PathLength;
+    PWCHAR RegPath;
+
+    VALIDATE_MMSYS_PARAMETER( ServiceName );
+    VALIDATE_MMSYS_PARAMETER( KeyHandle );
+
+    /*
+        Work out the space required to hold the path:
+
+        HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\
+            sndblst\
+                Parameters\
+                    Device123\
+    */
+    PathLength = wcslen(REG_SERVICES_KEY_NAME_U) + 1
+               + wcslen(ServiceName) + 1
+               + wcslen(REG_PARAMETERS_KEY_NAME_U) + 1
+               + wcslen(REG_DEVICE_KEY_NAME_U)
+               + GetDigitCount(DeviceIndex);
+
+    /* Allocate storage for the string */
+    RegPath = AllocateWideString(PathLength);
+
+    if ( ! RegPath )
+    {
+        return MMSYSERR_NOMEM;
+    }
+
+    /* Write the path */
+    wsprintf(RegPath,
+             L"%ls\\%ls\\%ls\\%ls%d",
+             REG_SERVICES_KEY_NAME_U,
+             ServiceName,
+             REG_PARAMETERS_KEY_NAME_U,
+             REG_DEVICE_KEY_NAME_U,
+             DeviceIndex);
+
+    SND_TRACE(L"Opening reg key: %wS\n", RegPath);
+
+    /* Perform the open */
+    if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+                      RegPath,
+                      0,
+                      KEY_READ,
+                      KeyHandle) != ERROR_SUCCESS )
+    {
+        /* Couldn't open the key */
+        SND_ERR(L"Failed to open reg key: %wS\n", RegPath);
+        FreeMemory(RegPath);
+        return MMSYSERR_ERROR;
+    }
+
+    FreeMemory(RegPath);
+
+    return MMSYSERR_NOERROR;
+}
diff --git a/reactos/lib/drivers/sound/shared/shared.rbuild b/reactos/lib/drivers/sound/shared/shared.rbuild
new file mode 100644 (file)
index 0000000..578f7a1
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<!DOCTYPE module SYSTEM "../../../../tools/rbuild/project.dtd">
+<module name="audio" type="staticlibrary" allowwarnings="true">
+    <define name="__NTDRIVER__"/>
+    <define name="KERNEL"/>
+    <include base="soundblaster">.</include>
+    <include base="ReactOS">include/reactos/libs/sound</include>
+    <file>time.c</file>
+</module>
diff --git a/reactos/lib/drivers/sound/shared/time.c b/reactos/lib/drivers/sound/shared/time.c
new file mode 100644 (file)
index 0000000..19b61bc
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+    ReactOS Sound System
+    Timing helper
+
+    Author:
+        Andrew Greenwood (silverblade@reactos.org)
+
+    History:
+        31 May 2008 - Created
+
+    Notes:
+        Have checked timing in DebugView. A 10,000ms delay covered a period
+        of 124.305 sec to 134.308 sec. Not 100% accurate but likely down to
+        the delays in submitting the debug strings?
+*/
+
+/*
+    Nanoseconds are fun! You must try some!
+    1 ns        = .000000001 seconds    = .0000001 ms
+    100 ns      = .0000001 seconds      = .00001 ms
+    10000 ns    = .00001 seconds        = .001 ms
+    1000000 ns  = .001 seconds          = 1 ms
+*/
+
+#include <ntddk.h>
+
+VOID
+SleepMs(ULONG Milliseconds)
+{
+    LARGE_INTEGER Period;
+
+    Period.QuadPart = -Milliseconds;
+    Period.QuadPart *= 10000;
+
+    KeDelayExecutionThread(KernelMode, FALSE, &Period);
+}
+
+ULONG
+QuerySystemTimeMs()
+{
+    LARGE_INTEGER Time;
+
+    KeQuerySystemTime(&Time);
+
+    Time.QuadPart /= 10000;
+
+    return (ULONG) Time.QuadPart;
+}
+
diff --git a/reactos/lib/drivers/sound/sound.rbuild b/reactos/lib/drivers/sound/sound.rbuild
new file mode 100644 (file)
index 0000000..7255a87
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<!DOCTYPE group SYSTEM "../../../tools/rbuild/project.dtd">
+<group xmlns:xi="http://www.w3.org/2001/XInclude">
+    <directory name="legacy">
+        <xi:include href="legacy/legacy.rbuild" />
+    </directory>
+    <directory name="shared">
+        <xi:include href="shared/shared.rbuild" />
+    </directory>
+    <directory name="soundblaster">
+        <xi:include href="soundblaster/soundblaster.rbuild" />
+    </directory>
+    <directory name="uartmidi">
+        <xi:include href="uartmidi/uartmidi.rbuild" />
+    </directory>
+    <directory name="mmebuddy">
+        <xi:include href="mmebuddy/mmebuddy.rbuild" />
+    </directory>
+    <directory name="mment4">
+        <xi:include href="mment4/mment4.rbuild" />
+    </directory>
+</group>
\ No newline at end of file
diff --git a/reactos/lib/drivers/sound/soundblaster/dsp_io.c b/reactos/lib/drivers/sound/soundblaster/dsp_io.c
new file mode 100644 (file)
index 0000000..e45d613
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+    ReactOS Sound System
+    Sound Blaster DSP support
+    General I/O
+
+    Author:
+        Andrew Greenwood (silverblade@reactos.org)
+
+    History:
+        2 July 2008 - Created (split from sbdsp.c)
+
+    Notes:
+        Functions documented in sbdsp.h
+*/
+
+#include <ntddk.h>
+#include <debug.h>
+
+#include <time.h>
+#include <sbdsp.h>
+
+NTSTATUS
+SbDspReset(
+    IN  PUCHAR BasePort,
+    IN  ULONG Timeout)
+{
+    ULONG Expiry;
+    KIRQL CurrentIrqLevel = KeGetCurrentIrql();
+    BOOLEAN DataAvailable = FALSE;
+
+    /* Should be called from DriverEntry with this IRQL */
+    ASSERT(CurrentIrqLevel == PASSIVE_LEVEL);
+
+    WRITE_SB_DSP_RESET(BasePort, 0x01);
+    SleepMs(50);   /* Should be enough */
+    WRITE_SB_DSP_RESET(BasePort, 0x00);
+
+    Expiry = QuerySystemTimeMs() + Timeout;
+
+    /* Wait for data to be available */
+    while ( (QuerySystemTimeMs() < Expiry) || ( Timeout == 0) )
+    {
+        if ( SB_DSP_DATA_AVAILABLE(BasePort) )
+        {
+            DataAvailable = TRUE;
+            break;
+        }
+    }
+
+    if ( ! DataAvailable )
+    {
+        return STATUS_TIMEOUT;
+    }
+
+    /* Data is available - wait for the "DSP ready" code */
+    while ( (QuerySystemTimeMs() < Expiry) || ( Timeout == 0) )
+    {
+        if ( READ_SB_DSP_DATA(BasePort) == SB_DSP_READY )
+        {
+            return STATUS_SUCCESS;
+        }
+    }
+
+    return STATUS_TIMEOUT;
+}
+
+NTSTATUS
+SbDspWaitToWrite(
+    IN  PUCHAR BasePort,
+    IN  ULONG Timeout)
+{
+    ULONG Expiry = QuerySystemTimeMs() + Timeout;
+
+    while ( (QuerySystemTimeMs() < Expiry) || (Timeout == 0) )
+    {
+        if ( SB_DSP_CLEAR_TO_SEND(BasePort) )
+        {
+            return STATUS_SUCCESS;
+        }
+    }
+
+    return STATUS_TIMEOUT;
+}
+
+NTSTATUS
+SbDspWaitToRead(
+    IN  PUCHAR BasePort,
+    IN  ULONG Timeout)
+{
+    ULONG Expiry = QuerySystemTimeMs() + Timeout;
+
+    while ( (QuerySystemTimeMs() < Expiry) || (Timeout == 0) )
+    {
+        if ( SB_DSP_DATA_AVAILABLE(BasePort) )
+        {
+            return STATUS_SUCCESS;
+        }
+    }
+
+    return STATUS_TIMEOUT;
+}
+
+NTSTATUS
+SbDspWrite(
+    IN  PUCHAR BasePort,
+    IN  UCHAR DataByte,
+    IN  ULONG Timeout)
+{
+    NTSTATUS Status;
+
+    Status = SbDspWaitToWrite(BasePort, Timeout);
+
+    if ( Status != STATUS_SUCCESS )
+    {
+        return Status;
+    }
+
+    DbgPrint("SBDSP - Writing %02x\n", DataByte);
+    WRITE_SB_DSP_DATA(BasePort, DataByte);
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+SbDspRead(
+    IN  PUCHAR BasePort,
+    OUT PUCHAR DataByte,
+    IN  ULONG Timeout)
+{
+    NTSTATUS Status;
+
+    if ( ! DataByte )
+    {
+        return STATUS_INVALID_PARAMETER_2;
+    }
+
+    Status = SbDspWaitToRead(BasePort, Timeout);
+
+    if ( Status != STATUS_SUCCESS )
+    {
+        return Status;
+    }
+
+    *DataByte = READ_SB_DSP_DATA(BasePort);
+    DbgPrint("SBDSP - Read %02x\n", *DataByte);
+
+    return STATUS_SUCCESS;
+}
diff --git a/reactos/lib/drivers/sound/soundblaster/mixer.c b/reactos/lib/drivers/sound/soundblaster/mixer.c
new file mode 100644 (file)
index 0000000..c6f9197
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+    ReactOS Sound System
+    Sound Blaster DSP support
+    Mixer routines
+
+    Author:
+        Andrew Greenwood (silverblade@reactos.org)
+
+    History:
+        2 July 2008 - Created
+
+    Notes:
+        Functions documented in sbdsp.h
+
+        Currently, input/output switches and PC speaker volume
+        level are not supported.
+
+        The I/O switches are used for muting/unmuting mic, etc.
+*/
+
+#include <ntddk.h>
+#include <debug.h>
+
+#include <sbdsp.h>
+
+VOID
+SbMixerReset(IN PUCHAR BasePort)
+{
+    WRITE_SB_MIXER_REGISTER(BasePort, SB_MIX_RESET);
+    /* Are we meant to send anything else? */
+}
+
+NTSTATUS
+SbMixerPackLevelData(
+    IN  UCHAR Line,
+    IN  UCHAR Level,
+    OUT PUCHAR PackedLevel)
+{
+    if ( ! PackedLevel )
+        return STATUS_INVALID_PARAMETER_3;
+
+    switch ( Line )
+    {
+        case SB_MIX_MASTER_LEFT_LEVEL :
+        case SB_MIX_MASTER_RIGHT_LEVEL :
+        case SB_MIX_VOC_LEFT_LEVEL :
+        case SB_MIX_VOC_RIGHT_LEVEL :
+        case SB_MIX_MIDI_LEFT_LEVEL :
+        case SB_MIX_MIDI_RIGHT_LEVEL :
+        case SB_MIX_CD_LEFT_LEVEL :
+        case SB_MIX_CD_RIGHT_LEVEL :
+        case SB_MIX_LINE_LEFT_LEVEL :
+        case SB_MIX_LINE_RIGHT_LEVEL :
+        case SB_MIX_MIC_LEVEL :
+        case SB_MIX_LEGACY_MIC_LEVEL :  /* is this correct? */
+        {
+            if ( Level >= 0x20 )
+                return STATUS_INVALID_PARAMETER_2;
+
+            *PackedLevel = Level << 3;
+            return STATUS_SUCCESS;
+        }
+
+        case SB_MIX_INPUT_LEFT_GAIN :
+        case SB_MIX_INPUT_RIGHT_GAIN :
+        case SB_MIX_OUTPUT_LEFT_GAIN :
+        case SB_MIX_OUTPUT_RIGHT_GAIN :
+        {
+            if ( Level >= 0x04 )
+                return STATUS_INVALID_PARAMETER_2;
+
+            *PackedLevel = Level << 6;
+            return STATUS_SUCCESS;
+        }
+
+        case SB_MIX_VOC_LEVEL :         /* legacy */
+        case SB_MIX_MASTER_LEVEL :
+        case SB_MIX_FM_LEVEL :
+        case SB_MIX_CD_LEVEL :
+        case SB_MIX_LINE_LEVEL :
+        case SB_MIX_TREBLE_LEFT_LEVEL : /* bass/treble */
+        case SB_MIX_TREBLE_RIGHT_LEVEL :
+        case SB_MIX_BASS_LEFT_LEVEL :
+        case SB_MIX_BASS_RIGHT_LEVEL :
+        {
+            if ( Level >= 0x10 )
+                return STATUS_INVALID_PARAMETER_2;
+
+            *PackedLevel = Level << 4;
+            return STATUS_SUCCESS;
+        }
+
+        default :
+            return STATUS_INVALID_PARAMETER_1;
+    };
+}
+
+NTSTATUS
+SbMixerUnpackLevelData(
+    IN  UCHAR Line,
+    IN  UCHAR PackedLevel,
+    OUT PUCHAR Level)
+{
+    if ( ! Level )
+        return STATUS_INVALID_PARAMETER_3;
+
+    switch ( Line )
+    {
+        case SB_MIX_MASTER_LEFT_LEVEL :
+        case SB_MIX_MASTER_RIGHT_LEVEL :
+        case SB_MIX_VOC_LEFT_LEVEL :
+        case SB_MIX_VOC_RIGHT_LEVEL :
+        case SB_MIX_MIDI_LEFT_LEVEL :
+        case SB_MIX_MIDI_RIGHT_LEVEL :
+        case SB_MIX_CD_LEFT_LEVEL :
+        case SB_MIX_CD_RIGHT_LEVEL :
+        case SB_MIX_LINE_LEFT_LEVEL :
+        case SB_MIX_LINE_RIGHT_LEVEL :
+        case SB_MIX_MIC_LEVEL :
+        {
+            *Level = PackedLevel >> 3;
+            return STATUS_SUCCESS;
+        }
+
+        case SB_MIX_INPUT_LEFT_GAIN :
+        case SB_MIX_INPUT_RIGHT_GAIN :
+        case SB_MIX_OUTPUT_LEFT_GAIN :
+        case SB_MIX_OUTPUT_RIGHT_GAIN :
+        {
+            *Level = PackedLevel >> 6;
+            return STATUS_SUCCESS;
+        }
+
+        case SB_MIX_VOC_LEVEL :         /* legacy */
+        case SB_MIX_MASTER_LEVEL :
+        case SB_MIX_FM_LEVEL :
+        case SB_MIX_CD_LEVEL :
+        case SB_MIX_LINE_LEVEL :
+        case SB_MIX_TREBLE_LEFT_LEVEL : /* bass/treble */
+        case SB_MIX_TREBLE_RIGHT_LEVEL :
+        case SB_MIX_BASS_LEFT_LEVEL :
+        case SB_MIX_BASS_RIGHT_LEVEL :
+        {
+            *Level = PackedLevel >> 4;
+            return STATUS_SUCCESS;
+        }
+
+        default :
+            return STATUS_INVALID_PARAMETER_1;
+    };
+}
+
+NTSTATUS
+SbMixerSetLevel(
+    IN  PUCHAR BasePort,
+    IN  UCHAR Line,
+    IN  UCHAR Level)
+{
+    UCHAR PackedLevel = 0;
+    NTSTATUS Status;
+
+    Status = SbMixerPackLevelData(Line, Level, &PackedLevel);
+
+    switch ( Status )
+    {
+        case STATUS_SUCCESS :
+            break;
+
+        case STATUS_INVALID_PARAMETER_1 :
+            return STATUS_INVALID_PARAMETER_2;
+
+        case STATUS_INVALID_PARAMETER_2 :
+            return STATUS_INVALID_PARAMETER_3;
+
+        default :
+            return Status;
+    };
+
+    DbgPrint("SbMixerSetLevel: Line 0x%x, raw level 0x%x, packed 0x%x\n", Line, Level, PackedLevel);
+
+    WRITE_SB_MIXER_REGISTER(BasePort, Line);
+    WRITE_SB_MIXER_DATA(BasePort, PackedLevel);
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+SbMixerGetLevel(
+    IN  PUCHAR BasePort,
+    IN  UCHAR Line,
+    OUT PUCHAR Level)
+{
+    UCHAR PackedLevel = 0;
+    NTSTATUS Status;
+
+    if ( ! Level )
+        return STATUS_INVALID_PARAMETER_3;
+
+    WRITE_SB_MIXER_REGISTER(BasePort, Line);
+    PackedLevel = READ_SB_MIXER_DATA(BasePort);
+
+    Status = SbMixerUnpackLevelData(Line, PackedLevel, Level);
+
+    switch ( Status )
+    {
+        case STATUS_SUCCESS :
+            break;
+
+        case STATUS_INVALID_PARAMETER_1 :
+            return STATUS_INVALID_PARAMETER_2;
+
+        case STATUS_INVALID_PARAMETER_2 :
+            return STATUS_INVALID_PARAMETER_3;
+
+        default :
+            return Status;
+    };
+
+    DbgPrint("SbMixerGetLevel: Line 0x%x, raw level 0x%x, packed 0x%x\n", Line, Level, PackedLevel);
+
+    return STATUS_SUCCESS;
+}
+
+VOID
+SbMixerEnableAGC(IN PUCHAR BasePort)
+{
+    /* Untested... */
+    WRITE_SB_MIXER_REGISTER(BasePort, SB_MIX_AGC);
+    WRITE_SB_MIXER_DATA(BasePort, 1);
+}
+
+VOID
+SbMixerDisableAGC(IN PUCHAR BasePort)
+{
+    /* Untested... */
+    WRITE_SB_MIXER_REGISTER(BasePort, SB_MIX_AGC);
+    WRITE_SB_MIXER_DATA(BasePort, 0);
+}
+
+BOOLEAN
+SbMixerIsAGCEnabled(IN PUCHAR BasePort)
+{
+    /* Untested... */
+    WRITE_SB_MIXER_REGISTER(BasePort, SB_MIX_AGC);
+    return (READ_SB_MIXER_DATA(BasePort) != 0);
+}
diff --git a/reactos/lib/drivers/sound/soundblaster/rate.c b/reactos/lib/drivers/sound/soundblaster/rate.c
new file mode 100644 (file)
index 0000000..e539e7e
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+    ReactOS Sound System
+    Sound Blaster DSP support
+    Sample rate routines
+
+    Author:
+        Andrew Greenwood (silverblade@reactos.org)
+
+    History:
+        2 July 2008 - Created (split from sbdsp.c)
+
+    Notes:
+        Functions documented in sbdsp.h
+*/
+
+#include <ntddk.h>
+#include <debug.h>
+
+#include <sbdsp.h>
+
+BOOLEAN
+SbDspIsValidInputRate(
+    IN  UCHAR MajorVersion,
+    IN  UCHAR MinorVersion,
+    IN  USHORT Rate,
+    IN  BOOLEAN Stereo)
+{
+    if ( MajorVersion == 1 )
+    {
+        if ( Stereo )
+            return FALSE;
+
+        return ( ( Rate >= 4000 ) && ( Rate <= 13000 ) );
+    }
+    else if ( MajorVersion == 2 )
+    {
+        if ( Stereo )
+            return FALSE;
+
+        if ( MinorVersion == 0 )
+            return ( ( Rate >= 4000 ) && ( Rate <= 15000 ) );
+        else
+            return ( ( Rate >= 4000 ) && ( Rate <= 44100 ) );
+    }
+    else if ( MajorVersion == 3 )
+    {
+        if ( Stereo )
+            return FALSE;
+
+        return ( ( Rate >= 4000 ) && ( Rate <= 13000 ) );
+    }
+    else /* 4.00 and above */
+    {
+        return ( ( Rate >= 5000 ) && ( Rate <= 44100 ) );
+    }
+}
+
+BOOLEAN
+SbDspIsValidOutputRate(
+    IN  UCHAR MajorVersion,
+    IN  UCHAR MinorVersion,
+    IN  USHORT Rate,
+    IN  BOOLEAN Stereo)
+{
+    if ( MajorVersion == 1 )
+    {
+        if ( Stereo )
+            return FALSE;
+
+        return ( ( Rate >= 4000 ) && ( Rate <= 23000 ) );
+    }
+    else if ( MajorVersion == 2 )
+    {
+        if ( Stereo )
+            return FALSE;
+
+        if ( MinorVersion == 0 )
+            return ( ( Rate >= 4000 ) && ( Rate <= 23000 ) );
+        else
+            return ( ( Rate >= 4000 ) && ( Rate <= 44100 ) );
+    }
+    else if ( MajorVersion == 3 )
+    {
+        if ( ! Stereo )
+            return ( ( Rate >= 4000 ) && ( Rate <= 44100 ) );
+        else
+            return ( ( Rate >= 11025 ) && ( Rate <= 22050 ) );
+    }
+    else /* 4.00 and above */
+    {
+        return ( ( Rate >= 5000 ) && ( Rate <= 44100 ) );
+    }
+}
+
+/* Internal routine - call only after submitting one of the rate commands */
+NTSTATUS
+SbDsp4WriteRate(
+    IN  PUCHAR BasePort,
+    IN  USHORT Rate,
+    IN  ULONG Timeout)
+{
+    NTSTATUS Status;
+
+    /* NOTE - No check for validity of rate! */
+
+    /* Write high byte */
+    Status = SbDspWrite(BasePort, (Rate & 0xff00) >> 8, Timeout);
+    if ( Status != STATUS_SUCCESS )
+        return Status;
+
+    /* Write low byte */
+    Status = SbDspWrite(BasePort, Rate & 0xff, Timeout);
+    if ( Status != STATUS_SUCCESS )
+        return Status;
+
+    return Status;
+}
+
+NTSTATUS
+SbDsp4SetOutputRate(
+    IN  PUCHAR BasePort,
+    IN  USHORT Rate,
+    IN  ULONG Timeout)
+{
+    NTSTATUS Status;
+
+    /* NOTE - No check for validity of rate! */
+
+    /* Prepare to write the output rate */
+    Status = SbDspWrite(BasePort, SB_DSP_OUTPUT_RATE, (Rate & 0xff00) >> 8);
+    if ( Status != STATUS_SUCCESS )
+        return Status;
+
+    return SbDsp4WriteRate(BasePort, Rate, Timeout);
+}
+
+NTSTATUS
+SbDsp4SetInputRate(
+    IN  PUCHAR BasePort,
+    IN  USHORT Rate,
+    IN  ULONG Timeout)
+{
+    NTSTATUS Status;
+
+    /* NOTE - No check for validity of rate! */
+
+    /* Prepare to write the input rate */
+    Status = SbDspWrite(BasePort, SB_DSP_OUTPUT_RATE, (Rate & 0xff00) >> 8);
+    if ( Status != STATUS_SUCCESS )
+        return Status;
+
+    return SbDsp4WriteRate(BasePort, Rate, Timeout);
+}
diff --git a/reactos/lib/drivers/sound/soundblaster/soundblaster.rbuild b/reactos/lib/drivers/sound/soundblaster/soundblaster.rbuild
new file mode 100644 (file)
index 0000000..7012724
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<!DOCTYPE module SYSTEM "../../../../tools/rbuild/project.dtd">
+<module name="soundblaster" type="staticlibrary" allowwarnings="true">
+    <define name="__NTDRIVER__"/>
+    <define name="KERNEL"/>
+    <include base="soundblaster">.</include>
+    <include base="ReactOS">include/reactos/libs/sound</include>
+    <file>dsp_io.c</file>
+    <file>version.c</file>
+    <file>speaker.c</file>
+    <file>rate.c</file>
+    <file>mixer.c</file>
+</module>
diff --git a/reactos/lib/drivers/sound/soundblaster/speaker.c b/reactos/lib/drivers/sound/soundblaster/speaker.c
new file mode 100644 (file)
index 0000000..a7dc605
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+    ReactOS Sound System
+    Sound Blaster DSP support
+    Speaker commands
+
+    Author:
+        Andrew Greenwood (silverblade@reactos.org)
+
+    History:
+        2 July 2008 - Created (split from sbdsp.c)
+
+    Notes:
+        Functions documented in sbdsp.h
+*/
+
+#include <ntddk.h>
+#include <debug.h>
+
+#include <sbdsp.h>
+
+NTSTATUS
+SbDspEnableSpeaker(
+    IN  PUCHAR BasePort,
+    IN  ULONG Timeout)
+{
+    return SbDspWrite(BasePort, SB_DSP_SPEAKER_ON, Timeout);
+}
+
+NTSTATUS
+SbDspDisableSpeaker(
+    IN  PUCHAR BasePort,
+    IN  ULONG Timeout)
+{
+    return SbDspWrite(BasePort, SB_DSP_SPEAKER_OFF, Timeout);
+}
+
+/*
+    VirtualBox doesn't seem to support this.
+*/
+NTSTATUS
+SbDspIsSpeakerEnabled(
+    IN  PUCHAR BasePort,
+    OUT PBOOLEAN IsEnabled,
+    IN  ULONG Timeout)
+{
+    NTSTATUS Status;
+    UCHAR SpeakerStatus = 0;
+
+    if ( ! IsEnabled )
+        return STATUS_INVALID_PARAMETER_2;
+
+    /* Request the speaker status */
+    Status = SbDspWrite(BasePort, SB_DSP_SPEAKER_STATUS, Timeout);
+    if ( Status != STATUS_SUCCESS )
+        return Status;
+
+    /* Obtain the status */
+    Status = SbDspRead(BasePort, &SpeakerStatus, Timeout);
+    if ( Status != STATUS_SUCCESS )
+        return Status;
+
+    DbgPrint("SBDSP - SpeakerStatus is %02x\n", SpeakerStatus);
+    *IsEnabled = (SpeakerStatus == 0xFF);
+
+    return STATUS_SUCCESS;
+}
diff --git a/reactos/lib/drivers/sound/soundblaster/version.c b/reactos/lib/drivers/sound/soundblaster/version.c
new file mode 100644 (file)
index 0000000..045d166
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+    ReactOS Sound System
+    Sound Blaster DSP support
+    Version routine
+
+    Author:
+        Andrew Greenwood (silverblade@reactos.org)
+
+    History:
+        2 July 2008 - Created (split from sbdsp.c)
+
+    Notes:
+        Functions documented in sbdsp.h
+*/
+
+#include <ntddk.h>
+#include <debug.h>
+
+#include <sbdsp.h>
+
+NTSTATUS
+SbDspGetVersion(
+    IN  PUCHAR BasePort,
+    OUT PUCHAR MajorVersion,
+    OUT PUCHAR MinorVersion,
+    IN  ULONG Timeout)
+{
+    NTSTATUS Status;
+
+    /* Make sure our parameters are sane */
+    if ( ! MajorVersion )
+        return STATUS_INVALID_PARAMETER_2;
+
+    if ( ! MinorVersion )
+        return STATUS_INVALID_PARAMETER_3;
+
+    /* Send version request */
+    Status = SbDspWrite(BasePort, SB_DSP_VERSION, Timeout);
+    if ( Status != STATUS_SUCCESS )
+        return Status;
+
+    /* Get the major version */
+    Status = SbDspRead(BasePort, MajorVersion, Timeout);
+    if ( Status != STATUS_SUCCESS )
+        return FALSE;
+
+    /* Get the minor version */
+    Status = SbDspRead(BasePort, MinorVersion, Timeout);
+    return Status;
+}
diff --git a/reactos/lib/drivers/sound/uartmidi/midiuart.c b/reactos/lib/drivers/sound/uartmidi/midiuart.c
new file mode 100644 (file)
index 0000000..9a10137
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+    ReactOS Sound System
+    MIDI UART support
+
+    Author:
+        Andrew Greenwood (silverblade@reactos.org)
+
+    History:
+        26 May 2008 - Created
+
+    Notes:
+        Functions documented in midiuart.h
+*/
+
+#include <ntddk.h>
+#include "midiuart.h"
+
+BOOLEAN
+WaitForMidiUartStatus(
+    IN  PUCHAR UartBasePort,
+    IN  UCHAR StatusFlags,
+    IN  ULONG Timeout)
+{
+    ULONG RemainingTime = Timeout;
+
+    while ( RemainingTime -- )
+    {
+        if ( READ_MIDIUART_STATUS(UartBasePort) & StatusFlags )
+        {
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+BOOLEAN
+WriteMidiUartByte(
+    IN  PUCHAR UartBasePort,
+    IN  UCHAR Data,
+    IN  ULONG Timeout)
+{
+    if ( ! WaitForMidiUartCTS(UartBasePort, Timeout) )
+    {
+        return FALSE;
+    }
+
+    WRITE_MIDIUART_DATA(UartBasePort, Data);
+
+    return TRUE;
+}
+
+BOOLEAN
+WriteMidiUartMulti(
+    IN  PUCHAR UartBasePort,
+    IN  PUCHAR Data,
+    IN  ULONG DataLength,
+    IN  ULONG Timeout)
+{
+    ULONG DataIndex;
+
+    for ( DataIndex = 0; DataIndex < DataLength; ++ DataIndex )
+    {
+        if ( ! WriteMidiUartByte(UartBasePort, Data[DataIndex], Timeout) )
+        {
+            /* We failed - don't try writing any more */
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+BOOLEAN
+ReadMidiUartByte(
+    IN  PUCHAR UartBasePort,
+    OUT UCHAR* Data,
+    IN  ULONG Timeout)
+{
+    if ( ! Data )
+    {
+        return FALSE;
+    }
+
+    if ( ! WaitForMidiUartDTR(UartBasePort, Timeout) )
+    {
+        return FALSE;
+    }
+
+    *Data = READ_MIDIUART_DATA(UartBasePort);
+
+    return TRUE;
+}
diff --git a/reactos/lib/drivers/sound/uartmidi/uartmidi.rbuild b/reactos/lib/drivers/sound/uartmidi/uartmidi.rbuild
new file mode 100644 (file)
index 0000000..bdfad11
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<!DOCTYPE module SYSTEM "../../../../tools/rbuild/project.dtd">
+<module name="uartmidi" type="staticlibrary" allowwarnings="true">
+    <define name="__NTDRIVER__"/>
+    <define name="KERNEL"/>
+    <include base="soundblaster">.</include>
+    <include base="ReactOS">include/reactos/libs/sound</include>
+    <file>midiuart.c</file>
+</module>