mirror of
https://github.com/reactos/reactos.git
synced 2025-07-31 10:01:43 +00:00
Create a branch for cmake bringup.
svn path=/branches/cmake-bringup/; revision=48236
This commit is contained in:
parent
a28e798006
commit
c424146e2c
20602 changed files with 0 additions and 1140137 deletions
482
lib/drivers/sound/legacy/devname.c
Normal file
482
lib/drivers/sound/legacy/devname.c
Normal file
|
@ -0,0 +1,482 @@
|
|||
/*
|
||||
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 <sndnames.h>
|
||||
#include <sndtypes.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 ( ! IS_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);
|
||||
}
|
66
lib/drivers/sound/legacy/hardware.c
Normal file
66
lib/drivers/sound/legacy/hardware.c
Normal file
|
@ -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;
|
||||
}
|
10
lib/drivers/sound/legacy/legacy.rbuild
Normal file
10
lib/drivers/sound/legacy/legacy.rbuild
Normal file
|
@ -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>
|
62
lib/drivers/sound/mmebuddy/auxiliary/auxMessage.c
Normal file
62
lib/drivers/sound/mmebuddy/auxiliary/auxMessage.c
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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 <sndtypes.h>
|
||||
|
||||
#include <mmebuddy.h>
|
||||
|
||||
/*
|
||||
Standard MME driver entry-point for messages relating to auxiliary devices.
|
||||
*/
|
||||
DWORD
|
||||
APIENTRY
|
||||
auxMessage(
|
||||
UINT DeviceId,
|
||||
UINT Message,
|
||||
DWORD_PTR PrivateHandle,
|
||||
DWORD_PTR Parameter1,
|
||||
DWORD_PTR 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;
|
||||
}
|
111
lib/drivers/sound/mmebuddy/capabilities.c
Normal file
111
lib/drivers/sound/mmebuddy/capabilities.c
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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 <sndtypes.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,
|
||||
IN DWORD DeviceId,
|
||||
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);
|
||||
|
||||
SND_ASSERT( IS_VALID_SOUND_DEVICE_TYPE(DeviceType) );
|
||||
|
||||
/* 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;
|
||||
}
|
||||
case AUX_DEVICE_TYPE :
|
||||
{
|
||||
GoodSize = CapabilitiesSize >= sizeof(AUXCAPS);
|
||||
break;
|
||||
}
|
||||
case MIXER_DEVICE_TYPE :
|
||||
{
|
||||
GoodSize = CapabilitiesSize >= sizeof(MIXERCAPS);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
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,
|
||||
DeviceId,
|
||||
Capabilities,
|
||||
CapabilitiesSize);
|
||||
}
|
371
lib/drivers/sound/mmebuddy/deviceinstance.c
Normal file
371
lib/drivers/sound/mmebuddy/deviceinstance.c
Normal file
|
@ -0,0 +1,371 @@
|
|||
/*
|
||||
* 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>
|
||||
|
||||
/*
|
||||
Restrain ourselves from flooding the kernel device!
|
||||
*/
|
||||
|
||||
#define SOUND_KERNEL_BUFFER_COUNT 10
|
||||
#define SOUND_KERNEL_BUFFER_SIZE 16384
|
||||
|
||||
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;
|
||||
|
||||
/* Use default frame size */
|
||||
NewInstance->FrameSize = SOUND_KERNEL_BUFFER_SIZE;
|
||||
/* Use default buffer count */
|
||||
NewInstance->BufferCount = SOUND_KERNEL_BUFFER_COUNT;
|
||||
|
||||
/* Provide the caller with the new instance pointer */
|
||||
*SoundDeviceInstance = NewInstance;
|
||||
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
VOID
|
||||
FreeSoundDeviceInstance(
|
||||
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
|
||||
{
|
||||
/*
|
||||
Device is marked as invalid by now, but we can still do some sanity
|
||||
checking.
|
||||
*/
|
||||
SND_ASSERT( SoundDeviceInstance->Thread == NULL );
|
||||
|
||||
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) );
|
||||
if ( ! MMSUCCESS(Result) )
|
||||
return TranslateInternalMmResult(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;
|
||||
|
||||
/* Initialise the members of the struct used by the sound thread */
|
||||
(*SoundDeviceInstance)->HeadWaveHeader = NULL;
|
||||
(*SoundDeviceInstance)->TailWaveHeader = NULL;
|
||||
|
||||
(*SoundDeviceInstance)->OutstandingBuffers = 0;
|
||||
|
||||
(*SoundDeviceInstance)->LoopsRemaining = 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 )
|
||||
{
|
||||
/* This indicates bad practice, really! If you can open, why not close?! */
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
/* Stop the streaming thread */
|
||||
if ( SoundDeviceInstance->Thread )
|
||||
{
|
||||
Result = DestroySoundThread(SoundDeviceInstance->Thread);
|
||||
SND_ASSERT( MMSUCCESS(Result) ); /* It should succeed! */
|
||||
if ( ! MMSUCCESS(Result ) )
|
||||
{
|
||||
return TranslateInternalMmResult(Result);
|
||||
}
|
||||
}
|
||||
|
||||
/* Blank this out here */
|
||||
SoundDeviceInstance->Thread = NULL;
|
||||
|
||||
/* Try and close the device */
|
||||
Result = FunctionTable->Close(SoundDeviceInstance, Handle);
|
||||
SND_ASSERT( MMSUCCESS(Result) ); /* It should succeed! */
|
||||
if ( ! MMSUCCESS(Result) )
|
||||
return TranslateInternalMmResult(Result);
|
||||
|
||||
/* Drop it from the list */
|
||||
Result = UnlistSoundDeviceInstance(SoundDeviceInstance);
|
||||
SND_ASSERT( MMSUCCESS(Result) ); /* It should succeed! */
|
||||
if ( ! MMSUCCESS(Result) )
|
||||
return TranslateInternalMmResult(Result);
|
||||
|
||||
FreeSoundDeviceInstance(SoundDeviceInstance);
|
||||
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
MMRESULT
|
||||
DestroyAllSoundDeviceInstances(
|
||||
IN PSOUND_DEVICE SoundDevice)
|
||||
{
|
||||
MMRESULT Result;
|
||||
PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
|
||||
|
||||
SoundDeviceInstance = SoundDevice->HeadInstance;
|
||||
|
||||
while ( SoundDeviceInstance )
|
||||
{
|
||||
Result = DestroySoundDeviceInstance(SoundDeviceInstance);
|
||||
SND_ASSERT( MMSUCCESS(Result) );
|
||||
SoundDeviceInstance = SoundDeviceInstance->Next;
|
||||
}
|
||||
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
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_PTR ClientCallback,
|
||||
IN DWORD_PTR 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;
|
||||
}
|
361
lib/drivers/sound/mmebuddy/devicelist.c
Normal file
361
lib/drivers/sound/mmebuddy/devicelist.c
Normal file
|
@ -0,0 +1,361 @@
|
|||
/*
|
||||
* 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 <sndtypes.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;
|
||||
}
|
60
lib/drivers/sound/mmebuddy/functiontable.c
Normal file
60
lib/drivers/sound/mmebuddy/functiontable.c
Normal file
|
@ -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.
|
||||
*/
|
||||
MMRESULT
|
||||
SetSoundDeviceFunctionTable(
|
||||
IN PSOUND_DEVICE SoundDevice,
|
||||
IN PMMFUNCTION_TABLE FunctionTable)
|
||||
{
|
||||
VALIDATE_MMSYS_PARAMETER( IsValidSoundDevice(SoundDevice) );
|
||||
VALIDATE_MMSYS_PARAMETER( FunctionTable );
|
||||
|
||||
/* Zero out the existing function table (if present) */
|
||||
ZeroMemory(&SoundDevice->FunctionTable, sizeof(MMFUNCTION_TABLE));
|
||||
|
||||
if ( ! FunctionTable )
|
||||
return MMSYSERR_INVALPARAM;
|
||||
|
||||
/* 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;
|
||||
}
|
136
lib/drivers/sound/mmebuddy/kernel.c
Normal file
136
lib/drivers/sound/mmebuddy/kernel.c
Normal file
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* 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());
|
||||
|
||||
SND_TRACE(L"Transferred %d bytes in Sync overlapped I/O\n", Transferred);
|
||||
|
||||
if ( BytesTransferred )
|
||||
*BytesTransferred = Transferred;
|
||||
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
75
lib/drivers/sound/mmebuddy/midi/midMessage.c
Normal file
75
lib/drivers/sound/mmebuddy/midi/midMessage.c
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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 <sndtypes.h>
|
||||
|
||||
#include <mmebuddy.h>
|
||||
|
||||
/*
|
||||
Standard MME driver entry-point for messages relating to MIDI input.
|
||||
*/
|
||||
DWORD
|
||||
APIENTRY
|
||||
midMessage(
|
||||
UINT DeviceId,
|
||||
UINT Message,
|
||||
DWORD_PTR PrivateHandle,
|
||||
DWORD_PTR Parameter1,
|
||||
DWORD_PTR 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;
|
||||
}
|
||||
|
||||
case DRV_QUERYDEVICEINTERFACESIZE :
|
||||
{
|
||||
Result = MmeGetDeviceInterfaceString(MIDI_IN_DEVICE_TYPE, DeviceId, NULL, 0, (DWORD*)Parameter1); //FIXME DWORD_PTR
|
||||
break;
|
||||
}
|
||||
|
||||
case DRV_QUERYDEVICEINTERFACE :
|
||||
{
|
||||
Result = MmeGetDeviceInterfaceString(MIDI_IN_DEVICE_TYPE, DeviceId, (LPWSTR)Parameter1, Parameter2, NULL); //FIXME DWORD_PTR
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SND_TRACE(L"midMessage returning MMRESULT %d\n", Result);
|
||||
|
||||
ReleaseEntrypointMutex(MIDI_IN_DEVICE_TYPE);
|
||||
|
||||
return Result;
|
||||
}
|
74
lib/drivers/sound/mmebuddy/midi/modMessage.c
Normal file
74
lib/drivers/sound/mmebuddy/midi/modMessage.c
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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 <sndtypes.h>
|
||||
|
||||
#include <mmebuddy.h>
|
||||
|
||||
/*
|
||||
Standard MME driver entry-point for messages relating to MIDI output.
|
||||
*/
|
||||
DWORD
|
||||
APIENTRY
|
||||
modMessage(
|
||||
UINT DeviceId,
|
||||
UINT Message,
|
||||
DWORD_PTR PrivateHandle,
|
||||
DWORD_PTR Parameter1,
|
||||
DWORD_PTR 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;
|
||||
}
|
||||
|
||||
case DRV_QUERYDEVICEINTERFACESIZE :
|
||||
{
|
||||
Result = MmeGetDeviceInterfaceString(MIDI_OUT_DEVICE_TYPE, DeviceId, NULL, 0, (DWORD*)Parameter1); //FIXME DWORD_PTR
|
||||
break;
|
||||
}
|
||||
|
||||
case DRV_QUERYDEVICEINTERFACE :
|
||||
{
|
||||
Result = MmeGetDeviceInterfaceString(MIDI_OUT_DEVICE_TYPE, DeviceId, (LPWSTR)Parameter1, Parameter2, NULL); //FIXME DWORD_PTR
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SND_TRACE(L"modMessage returning MMRESULT %d\n", Result);
|
||||
|
||||
ReleaseEntrypointMutex(MIDI_OUT_DEVICE_TYPE);
|
||||
|
||||
return Result;
|
||||
}
|
266
lib/drivers/sound/mmebuddy/mixer/mxdMessage.c
Normal file
266
lib/drivers/sound/mmebuddy/mixer/mxdMessage.c
Normal file
|
@ -0,0 +1,266 @@
|
|||
/*
|
||||
* 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 <sndtypes.h>
|
||||
#undef NDEBUG
|
||||
#include <mmebuddy.h>
|
||||
|
||||
MMRESULT
|
||||
MmeGetLineInfo(
|
||||
IN UINT Message,
|
||||
IN DWORD_PTR PrivateHandle,
|
||||
IN DWORD_PTR Parameter1,
|
||||
IN DWORD_PTR Parameter2)
|
||||
{
|
||||
MMRESULT Result;
|
||||
PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
|
||||
PSOUND_DEVICE SoundDevice;
|
||||
PMMFUNCTION_TABLE FunctionTable;
|
||||
|
||||
//SND_TRACE(L"Getting mixer info %u\n", Message);
|
||||
|
||||
VALIDATE_MMSYS_PARAMETER( PrivateHandle );
|
||||
SoundDeviceInstance = (PSOUND_DEVICE_INSTANCE) PrivateHandle;
|
||||
|
||||
Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
|
||||
if ( ! MMSUCCESS(Result) )
|
||||
return TranslateInternalMmResult(Result);
|
||||
|
||||
Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
|
||||
if ( ! MMSUCCESS(Result) )
|
||||
return TranslateInternalMmResult(Result);
|
||||
|
||||
if ( ! FunctionTable->QueryMixerInfo )
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
|
||||
Result = FunctionTable->QueryMixerInfo(SoundDeviceInstance, Message, (LPVOID)Parameter1, Parameter2);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
||||
MMRESULT
|
||||
MmeCloseMixerDevice(
|
||||
IN DWORD_PTR PrivateHandle)
|
||||
{
|
||||
MMRESULT Result;
|
||||
PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
|
||||
PSOUND_DEVICE SoundDevice;
|
||||
|
||||
SND_TRACE(L"Closing mixer device \n");
|
||||
|
||||
VALIDATE_MMSYS_PARAMETER( PrivateHandle );
|
||||
SoundDeviceInstance = (PSOUND_DEVICE_INSTANCE) PrivateHandle;
|
||||
|
||||
if ( ! IsValidSoundDeviceInstance(SoundDeviceInstance) )
|
||||
return MMSYSERR_INVALHANDLE;
|
||||
|
||||
Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
|
||||
if ( ! MMSUCCESS(Result) )
|
||||
return TranslateInternalMmResult(Result);
|
||||
|
||||
|
||||
Result = DestroySoundDeviceInstance(SoundDeviceInstance);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
MMRESULT
|
||||
MmeOpenMixerDevice(
|
||||
IN MMDEVICE_TYPE DeviceType,
|
||||
IN DWORD DeviceId,
|
||||
IN LPMIXEROPENDESC OpenParameters,
|
||||
IN DWORD Flags,
|
||||
OUT DWORD* PrivateHandle)
|
||||
{
|
||||
MMRESULT Result;
|
||||
PMMFUNCTION_TABLE FunctionTable;
|
||||
PSOUND_DEVICE SoundDevice;
|
||||
PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
|
||||
|
||||
SND_TRACE(L"Opening mixer device");
|
||||
|
||||
VALIDATE_MMSYS_PARAMETER( OpenParameters );
|
||||
|
||||
Result = GetSoundDevice(DeviceType, DeviceId, &SoundDevice);
|
||||
if ( ! MMSUCCESS(Result) )
|
||||
return TranslateInternalMmResult(Result);
|
||||
|
||||
/* 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 = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
|
||||
if ( ! MMSUCCESS(Result) )
|
||||
return TranslateInternalMmResult(Result);
|
||||
|
||||
if ( ! FunctionTable->SetWaveFormat )
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
|
||||
Result = FunctionTable->SetWaveFormat(SoundDeviceInstance, DeviceId, NULL, 0);
|
||||
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_PTR) SoundDeviceInstance;
|
||||
|
||||
/* Store the additional information we were given - FIXME: Need flags! */
|
||||
SetSoundDeviceInstanceMmeData(SoundDeviceInstance,
|
||||
(HDRVR)OpenParameters->hmx,
|
||||
OpenParameters->dwCallback,
|
||||
OpenParameters->dwInstance,
|
||||
Flags);
|
||||
|
||||
/* Let the application know the device is open */
|
||||
ReleaseEntrypointMutex(DeviceType);
|
||||
#if 0
|
||||
NotifyMmeClient(SoundDeviceInstance,
|
||||
DeviceType == WAVE_OUT_DEVICE_TYPE ? WOM_OPEN : WIM_OPEN,
|
||||
0);
|
||||
#endif
|
||||
AcquireEntrypointMutex(DeviceType);
|
||||
|
||||
SND_TRACE(L"Mixer device now open\n");
|
||||
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
Standard MME driver entry-point for messages relating to mixers.
|
||||
*/
|
||||
DWORD
|
||||
APIENTRY
|
||||
mxdMessage(
|
||||
UINT DeviceId,
|
||||
UINT Message,
|
||||
DWORD_PTR PrivateHandle,
|
||||
DWORD_PTR Parameter1,
|
||||
DWORD_PTR 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;
|
||||
}
|
||||
|
||||
case MXDM_INIT :
|
||||
{
|
||||
Result = MMSYSERR_NOERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
case MXDM_OPEN :
|
||||
{
|
||||
Result = MmeOpenMixerDevice(MIXER_DEVICE_TYPE,
|
||||
DeviceId,
|
||||
(LPMIXEROPENDESC) Parameter1,
|
||||
Parameter2,
|
||||
(DWORD*) PrivateHandle);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MXDM_CLOSE :
|
||||
{
|
||||
Result = MmeCloseMixerDevice(PrivateHandle);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MXDM_GETCONTROLDETAILS :
|
||||
{
|
||||
Result = MmeGetLineInfo(Message,
|
||||
PrivateHandle,
|
||||
Parameter1,
|
||||
Parameter2);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MXDM_SETCONTROLDETAILS :
|
||||
{
|
||||
Result = MmeGetLineInfo(Message,
|
||||
PrivateHandle,
|
||||
Parameter1,
|
||||
Parameter2);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MXDM_GETLINECONTROLS :
|
||||
{
|
||||
Result = MmeGetLineInfo(Message,
|
||||
PrivateHandle,
|
||||
Parameter1,
|
||||
Parameter2);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MXDM_GETLINEINFO :
|
||||
{
|
||||
Result = MmeGetLineInfo(Message,
|
||||
PrivateHandle,
|
||||
Parameter1,
|
||||
Parameter2);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case DRV_QUERYDEVICEINTERFACESIZE :
|
||||
{
|
||||
Result = MmeGetDeviceInterfaceString(MIXER_DEVICE_TYPE, DeviceId, NULL, 0, (DWORD*)Parameter1); //FIXME DWORD_PTR
|
||||
break;
|
||||
}
|
||||
|
||||
case DRV_QUERYDEVICEINTERFACE :
|
||||
{
|
||||
Result = MmeGetDeviceInterfaceString(MIXER_DEVICE_TYPE, DeviceId, (LPWSTR)Parameter1, Parameter2, NULL); //FIXME DWORD_PTR
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//SND_TRACE(L"mxdMessage returning MMRESULT %d\n", Result);
|
||||
|
||||
ReleaseEntrypointMutex(MIXER_DEVICE_TYPE);
|
||||
|
||||
return Result;
|
||||
}
|
32
lib/drivers/sound/mmebuddy/mmebuddy.rbuild
Normal file
32
lib/drivers/sound/mmebuddy/mmebuddy.rbuild
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?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="NDEBUG">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>
|
||||
<file>streaming.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>
|
324
lib/drivers/sound/mmebuddy/mmewrap.c
Normal file
324
lib/drivers/sound/mmebuddy/mmewrap.c
Normal file
|
@ -0,0 +1,324 @@
|
|||
/*
|
||||
* 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 <sndtypes.h>
|
||||
#include <mmebuddy.h>
|
||||
|
||||
|
||||
/*
|
||||
Sets the device into running or stopped state
|
||||
*/
|
||||
|
||||
MMRESULT
|
||||
MmeSetState(
|
||||
IN DWORD_PTR PrivateHandle,
|
||||
IN BOOL bStart)
|
||||
{
|
||||
MMRESULT Result;
|
||||
PMMFUNCTION_TABLE FunctionTable;
|
||||
PSOUND_DEVICE SoundDevice;
|
||||
PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
|
||||
|
||||
VALIDATE_MMSYS_PARAMETER( PrivateHandle );
|
||||
SoundDeviceInstance = (PSOUND_DEVICE_INSTANCE) PrivateHandle;
|
||||
|
||||
VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
|
||||
|
||||
Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
|
||||
if ( ! MMSUCCESS(Result) )
|
||||
return TranslateInternalMmResult(Result);
|
||||
|
||||
/* Get the function table, and validate it */
|
||||
Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
|
||||
if ( ! MMSUCCESS(Result) )
|
||||
return TranslateInternalMmResult(Result);
|
||||
|
||||
SND_ASSERT( FunctionTable->SetState );
|
||||
if ( FunctionTable->SetState == NULL )
|
||||
{
|
||||
/* FIXME */
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
/* Try change state */
|
||||
Result = FunctionTable->SetState(SoundDeviceInstance, bStart);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
/*
|
||||
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 UINT Message,
|
||||
IN DWORD_PTR 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 ID */
|
||||
VALIDATE_MMSYS_PARAMETER( Capabilities );
|
||||
VALIDATE_MMSYS_PARAMETER( IS_VALID_SOUND_DEVICE_TYPE(DeviceType) );
|
||||
|
||||
/* Our parameter checks are done elsewhere */
|
||||
|
||||
Result = GetSoundDevice(DeviceType, DeviceId, &SoundDevice);
|
||||
|
||||
if ( ! MMSUCCESS(Result) )
|
||||
return Result;
|
||||
|
||||
return GetSoundDeviceCapabilities(SoundDevice,
|
||||
DeviceId,
|
||||
Capabilities,
|
||||
CapabilitiesSize);
|
||||
}
|
||||
|
||||
MMRESULT
|
||||
MmeOpenWaveDevice(
|
||||
IN MMDEVICE_TYPE DeviceType,
|
||||
IN UINT DeviceId,
|
||||
IN LPWAVEOPENDESC OpenParameters,
|
||||
IN DWORD Flags,
|
||||
OUT DWORD_PTR* 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, DeviceId, Format, sizeof(WAVEFORMATEX));
|
||||
if ( ! MMSUCCESS(Result) )
|
||||
{
|
||||
/* TODO: Destroy sound instance */
|
||||
return TranslateInternalMmResult(Result);
|
||||
}
|
||||
|
||||
/* Store the device instance pointer in the private handle */
|
||||
*PrivateHandle = (DWORD_PTR)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_PTR 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;
|
||||
|
||||
if ( ! IsValidSoundDeviceInstance(SoundDeviceInstance) )
|
||||
return MMSYSERR_INVALHANDLE;
|
||||
|
||||
Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
|
||||
if ( ! MMSUCCESS(Result) )
|
||||
return TranslateInternalMmResult(Result);
|
||||
|
||||
Result = GetSoundDeviceType(SoundDevice, &DeviceType);
|
||||
if ( ! MMSUCCESS(Result) )
|
||||
return TranslateInternalMmResult(Result);
|
||||
|
||||
|
||||
/* TODO: Check device is stopped! */
|
||||
|
||||
ReleaseEntrypointMutex(DeviceType);
|
||||
/* TODO: Work with MIDI devices too */
|
||||
NotifyMmeClient(SoundDeviceInstance,
|
||||
DeviceType == WAVE_OUT_DEVICE_TYPE ? WOM_CLOSE : WIM_CLOSE,
|
||||
0);
|
||||
AcquireEntrypointMutex(DeviceType);
|
||||
|
||||
Result = DestroySoundDeviceInstance(SoundDeviceInstance);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
MMRESULT
|
||||
MmeResetWavePlayback(
|
||||
IN DWORD_PTR PrivateHandle)
|
||||
{
|
||||
PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
|
||||
|
||||
SND_TRACE(L"Resetting wave device (WODM_RESET)\n");
|
||||
|
||||
VALIDATE_MMSYS_PARAMETER( PrivateHandle );
|
||||
SoundDeviceInstance = (PSOUND_DEVICE_INSTANCE) PrivateHandle;
|
||||
|
||||
return StopStreaming(SoundDeviceInstance);
|
||||
}
|
||||
|
||||
MMRESULT
|
||||
MmeGetDeviceInterfaceString(
|
||||
IN MMDEVICE_TYPE DeviceType,
|
||||
IN DWORD DeviceId,
|
||||
IN LPWSTR Interface,
|
||||
IN DWORD InterfaceLength,
|
||||
OUT DWORD * InterfaceSize)
|
||||
{
|
||||
MMRESULT Result;
|
||||
PSOUND_DEVICE SoundDevice;
|
||||
PMMFUNCTION_TABLE FunctionTable;
|
||||
|
||||
Result = GetSoundDevice(DeviceType, DeviceId, &SoundDevice);
|
||||
if ( ! MMSUCCESS(Result) )
|
||||
return TranslateInternalMmResult(Result);
|
||||
|
||||
Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
|
||||
if ( ! MMSUCCESS(Result) )
|
||||
return TranslateInternalMmResult(Result);
|
||||
|
||||
if ( FunctionTable->GetDeviceInterfaceString == NULL )
|
||||
{
|
||||
/* querying device interface string / size not supported */
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
/* Call the driver */
|
||||
Result = FunctionTable->GetDeviceInterfaceString(DeviceType, DeviceId, Interface, InterfaceLength, InterfaceSize);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
||||
MMRESULT
|
||||
MmeGetPosition(
|
||||
IN MMDEVICE_TYPE DeviceType,
|
||||
IN DWORD DeviceId,
|
||||
IN DWORD_PTR PrivateHandle,
|
||||
IN MMTIME* Time,
|
||||
IN DWORD Size)
|
||||
{
|
||||
MMRESULT Result;
|
||||
PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
|
||||
PSOUND_DEVICE SoundDevice;
|
||||
PMMFUNCTION_TABLE FunctionTable;
|
||||
|
||||
VALIDATE_MMSYS_PARAMETER( PrivateHandle );
|
||||
SoundDeviceInstance = (PSOUND_DEVICE_INSTANCE) PrivateHandle;
|
||||
|
||||
if ( ! IsValidSoundDeviceInstance(SoundDeviceInstance) )
|
||||
return MMSYSERR_INVALHANDLE;
|
||||
|
||||
Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
|
||||
if ( ! MMSUCCESS(Result) )
|
||||
return TranslateInternalMmResult(Result);
|
||||
|
||||
if ( Size != sizeof(MMTIME) )
|
||||
return MMSYSERR_INVALPARAM;
|
||||
|
||||
Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
|
||||
if ( ! MMSUCCESS(Result) )
|
||||
return TranslateInternalMmResult(Result);
|
||||
|
||||
if ( FunctionTable->GetPos == NULL )
|
||||
{
|
||||
/* This indicates bad practice, really! If you can open, why not close?! */
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
/* Call the driver */
|
||||
Result = FunctionTable->GetPos(SoundDeviceInstance, Time);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
90
lib/drivers/sound/mmebuddy/readme.txt
Normal file
90
lib/drivers/sound/mmebuddy/readme.txt
Normal file
|
@ -0,0 +1,90 @@
|
|||
MME BUDDY
|
||||
|
||||
This library currently is capable of maintaining lists of devices for all of
|
||||
the MME types, it will provide the appropriate entrypoints for each device
|
||||
type, and code using this library simply needs to inform the MME Buddy
|
||||
library of the devices that exist, and provide callback routines to be used
|
||||
when opening/closing/playing, etc.
|
||||
|
||||
Code using this library needs to provide its own DriverProc entrypoint (this
|
||||
may be refactored in future so that simply an init/cleanup routine need be
|
||||
provided.)
|
||||
|
||||
|
||||
WAVE OUTPUT
|
||||
===========
|
||||
Supported MME messages:
|
||||
* WODM_GETNUMDEVS (Get number of devices)
|
||||
* WODM_GETDEVCAPS (Get device capabilities)
|
||||
* WODM_OPEN (Open a device, query supported formats)
|
||||
* WODM_CLOSE (Close a device)
|
||||
* WODM_PREPARE (Prepare a wave header)
|
||||
* WODM_UNPREPARE (Unprepare a wave header)
|
||||
* WODM_WRITE (Submit a prepared header to be played)
|
||||
|
||||
Unsupported MME messages:
|
||||
* Any not mentioned above
|
||||
|
||||
Notes/Bugs:
|
||||
* WHDR_BEGINLOOP and WHDR_ENDLOOP are ignored
|
||||
* Not possible to pause/restart playback
|
||||
|
||||
|
||||
WAVE INPUT
|
||||
==========
|
||||
Supported MME messages:
|
||||
* WIDM_GETNUMDEVS (Get number of devices)
|
||||
|
||||
Unsupported MME messages:
|
||||
* Any not mentioned above
|
||||
|
||||
Notes/Bugs:
|
||||
* Mostly unimplemented
|
||||
|
||||
|
||||
MIDI OUTPUT
|
||||
===========
|
||||
Supported MME messages:
|
||||
* MODM_GETNUMDEVS (Get number of devices)
|
||||
|
||||
Unsupported MME messages:
|
||||
* Any not mentioned above
|
||||
|
||||
Notes/Bugs:
|
||||
* Mostly unimplemented
|
||||
|
||||
|
||||
MIDI INPUT
|
||||
==========
|
||||
Supported MME messages:
|
||||
* MIDM_GETNUMDEVS (Get number of devices)
|
||||
|
||||
Unsupported MME messages:
|
||||
* Any not mentioned above
|
||||
|
||||
Notes/Bugs:
|
||||
* Mostly unimplemented
|
||||
|
||||
|
||||
AUXILIARY
|
||||
=========
|
||||
Supported MME messages:
|
||||
* AUXM_GETNUMDEVS (Get number of devices)
|
||||
|
||||
Unsupported MME messages:
|
||||
* Any not mentioned above
|
||||
|
||||
Notes/Bugs:
|
||||
* Mostly unimplemented
|
||||
|
||||
|
||||
MIXER
|
||||
=====
|
||||
Supported MME messages:
|
||||
* MXDM_GETNUMDEVS (Get number of devices)
|
||||
|
||||
Unsupported MME messages:
|
||||
* Any not mentioned above
|
||||
|
||||
Notes/Bugs:
|
||||
* Mostly unimplemented
|
108
lib/drivers/sound/mmebuddy/reentrancy.c
Normal file
108
lib/drivers/sound/mmebuddy/reentrancy.c
Normal file
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* 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 <sndtypes.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]);
|
||||
}
|
308
lib/drivers/sound/mmebuddy/thread.c
Normal file
308
lib/drivers/sound/mmebuddy/thread.c
Normal file
|
@ -0,0 +1,308 @@
|
|||
/*
|
||||
* 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? What do we do here? Stream stuff? */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This should not happen! */
|
||||
SND_ASSERT(FALSE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SND_TRACE(L"Sound thread terminated\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MMRESULT
|
||||
CallSoundThread(
|
||||
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
|
||||
IN SOUND_THREAD_REQUEST_HANDLER RequestHandler,
|
||||
IN PVOID Parameter OPTIONAL)
|
||||
{
|
||||
PSOUND_THREAD Thread;
|
||||
|
||||
VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
|
||||
VALIDATE_MMSYS_PARAMETER( RequestHandler );
|
||||
|
||||
Thread = SoundDeviceInstance->Thread;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
MMRESULT
|
||||
SoundThreadTerminator(
|
||||
IN PSOUND_DEVICE_INSTANCE Instance,
|
||||
IN PVOID Parameter)
|
||||
{
|
||||
PSOUND_THREAD Thread = (PSOUND_THREAD) Parameter;
|
||||
|
||||
SND_TRACE(L"Sound thread terminator routine called\n");
|
||||
SND_ASSERT( Thread );
|
||||
|
||||
Thread->Running = FALSE;
|
||||
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
MMRESULT
|
||||
TerminateSoundThread(
|
||||
IN PSOUND_THREAD Thread)
|
||||
{
|
||||
DWORD WaitResult;
|
||||
|
||||
SND_ASSERT( Thread );
|
||||
|
||||
SND_TRACE(L"Waiting for READY event\n");
|
||||
WaitForSingleObject(Thread->Events.Ready, INFINITE);
|
||||
|
||||
Thread->Request.Result = MMSYSERR_NOTSUPPORTED;
|
||||
Thread->Request.Handler = SoundThreadTerminator;
|
||||
Thread->Request.SoundDeviceInstance = NULL;
|
||||
Thread->Request.Parameter = (PVOID) Thread;
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Wait for the thread to actually end */
|
||||
WaitResult = WaitForSingleObject(Thread->Handle, INFINITE);
|
||||
SND_ASSERT( WaitResult == WAIT_OBJECT_0 );
|
||||
|
||||
/* Close the thread and invalidate the handle */
|
||||
CloseHandle(Thread->Handle); /* Is this needed? */
|
||||
Thread->Handle = INVALID_HANDLE_VALUE;
|
||||
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
|
||||
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 )
|
||||
{
|
||||
SND_ERR(L"Failed to resume thread!\n");
|
||||
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)
|
||||
{
|
||||
VALIDATE_MMSYS_PARAMETER( Thread );
|
||||
SND_ASSERT( Thread->Handle != INVALID_HANDLE_VALUE );
|
||||
|
||||
SND_TRACE(L"Terminating sound thread\n");
|
||||
|
||||
/* Tell the thread to terminate itself */
|
||||
TerminateSoundThread(Thread);
|
||||
|
||||
SND_TRACE(L"Sound thread terminated, performing cleanup of thread resources\n");
|
||||
|
||||
CloseHandle(Thread->Handle); /* Is this needed? */
|
||||
Thread->Handle = INVALID_HANDLE_VALUE;
|
||||
|
||||
DestroySoundThreadEvents(Thread->Events.Ready,
|
||||
Thread->Events.Request,
|
||||
Thread->Events.Done);
|
||||
|
||||
/* Wipe and free the memory used for the thread */
|
||||
ZeroMemory(Thread, sizeof(SOUND_THREAD));
|
||||
FreeMemory(Thread);
|
||||
|
||||
SND_TRACE(L"Finished thread cleanup\n");
|
||||
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
149
lib/drivers/sound/mmebuddy/utility.c
Normal file
149
lib/drivers/sound/mmebuddy/utility.c
Normal file
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
case ERROR_INVALID_PARAMETER :
|
||||
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;
|
||||
}
|
92
lib/drivers/sound/mmebuddy/wave/format.c
Normal file
92
lib/drivers/sound/mmebuddy/wave/format.c
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* 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 <sndtypes.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 DWORD DeviceId,
|
||||
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, DeviceId, Format, FormatSize);
|
||||
}
|
363
lib/drivers/sound/mmebuddy/wave/header.c
Normal file
363
lib/drivers/sound/mmebuddy/wave/header.c
Normal file
|
@ -0,0 +1,363 @@
|
|||
/*
|
||||
* PROJECT: ReactOS Sound System "MME Buddy" Library
|
||||
* LICENSE: GPL - See COPYING in the top level directory
|
||||
* FILE: lib/drivers/sound/mmebuddy/wave/header.c
|
||||
*
|
||||
* PURPOSE: Wave header preparation and submission routines
|
||||
*
|
||||
* PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#include <mmddk.h>
|
||||
#include <ntddsnd.h>
|
||||
#include <mmebuddy.h>
|
||||
#include <sndtypes.h>
|
||||
|
||||
|
||||
/*
|
||||
This structure gets used locally within functions as a way to shuttle data
|
||||
to the sound thread. It's safe to use locally since CallSoundThread will
|
||||
not return until the operation has been carried out.
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MMWAVEHEADER_FUNC Function;
|
||||
PWAVEHDR Header;
|
||||
} THREADED_WAVEHEADER_PARAMETERS;
|
||||
|
||||
|
||||
/*
|
||||
Helper routines to simplify the call to the sound thread for the header
|
||||
functions.
|
||||
*/
|
||||
|
||||
MMRESULT
|
||||
WaveHeaderOperationInSoundThread(
|
||||
PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
|
||||
IN PVOID Parameter)
|
||||
{
|
||||
THREADED_WAVEHEADER_PARAMETERS* Parameters = (THREADED_WAVEHEADER_PARAMETERS*) Parameter;
|
||||
return Parameters->Function(SoundDeviceInstance, Parameters->Header);
|
||||
}
|
||||
|
||||
MMRESULT
|
||||
WaveHeaderOperation(
|
||||
MMWAVEHEADER_FUNC Function,
|
||||
PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
|
||||
PWAVEHDR Header)
|
||||
{
|
||||
THREADED_WAVEHEADER_PARAMETERS Parameters;
|
||||
|
||||
Parameters.Function = Function;
|
||||
Parameters.Header = Header;
|
||||
|
||||
return CallSoundThread(SoundDeviceInstance,
|
||||
WaveHeaderOperationInSoundThread,
|
||||
&Parameters);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
SanitizeWaveHeader
|
||||
Clean up a header / reinitialize
|
||||
*/
|
||||
|
||||
VOID
|
||||
SanitizeWaveHeader(
|
||||
PWAVEHDR Header)
|
||||
{
|
||||
PWAVEHDR_EXTENSION Extension = (PWAVEHDR_EXTENSION) Header->reserved;
|
||||
SND_ASSERT( Extension );
|
||||
|
||||
Header->dwBytesRecorded = 0;
|
||||
|
||||
Extension->BytesCommitted = 0;
|
||||
Extension->BytesCompleted = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
The following routines are basically handlers for:
|
||||
- WODM_PREPARE
|
||||
- WODM_UNPREPARE
|
||||
- WODM_WRITE
|
||||
|
||||
All of these calls are ultimately dealt with in the context of the
|
||||
appropriate sound thread, so the implementation should expect itself to
|
||||
be running in this other thread when any of these operations take place.
|
||||
*/
|
||||
|
||||
MMRESULT
|
||||
PrepareWaveHeader(
|
||||
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
|
||||
IN PWAVEHDR Header)
|
||||
{
|
||||
MMRESULT Result;
|
||||
PSOUND_DEVICE SoundDevice;
|
||||
PMMFUNCTION_TABLE FunctionTable;
|
||||
PWAVEHDR_EXTENSION Extension;
|
||||
|
||||
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);
|
||||
|
||||
Extension = AllocateStruct(WAVEHDR_EXTENSION);
|
||||
if ( ! Extension )
|
||||
return MMSYSERR_NOMEM;
|
||||
|
||||
Header->reserved = (DWORD_PTR) Extension;
|
||||
Extension->BytesCommitted = 0;
|
||||
Extension->BytesCompleted = 0;
|
||||
|
||||
/* Configure the flags */
|
||||
Header->dwFlags |= WHDR_PREPARED;
|
||||
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
MMRESULT
|
||||
UnprepareWaveHeader(
|
||||
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
|
||||
IN PWAVEHDR Header)
|
||||
{
|
||||
MMRESULT Result;
|
||||
PSOUND_DEVICE SoundDevice;
|
||||
PMMFUNCTION_TABLE FunctionTable;
|
||||
PWAVEHDR_EXTENSION Extension;
|
||||
|
||||
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);
|
||||
|
||||
SND_ASSERT( Header->reserved );
|
||||
Extension = (PWAVEHDR_EXTENSION) Header->reserved;
|
||||
FreeMemory(Extension);
|
||||
|
||||
/* Configure the flags */
|
||||
Header->dwFlags &= ~WHDR_PREPARED;
|
||||
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
MMRESULT
|
||||
WriteWaveHeader(
|
||||
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->CommitWaveBuffer )
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
|
||||
/*
|
||||
A few minor sanity checks - any custom checks should've been carried
|
||||
out during wave header preparation etc.
|
||||
*/
|
||||
VALIDATE_MMSYS_PARAMETER( Header->lpData != NULL );
|
||||
VALIDATE_MMSYS_PARAMETER( Header->dwBufferLength > 0 );
|
||||
VALIDATE_MMSYS_PARAMETER( Header->dwFlags & WHDR_PREPARED );
|
||||
VALIDATE_MMSYS_PARAMETER( ! (Header->dwFlags & WHDR_INQUEUE) );
|
||||
|
||||
SanitizeWaveHeader(Header);
|
||||
|
||||
/* Clear the "done" flag for the buffer */
|
||||
Header->dwFlags &= ~WHDR_DONE;
|
||||
|
||||
Result = CallSoundThread(SoundDeviceInstance,
|
||||
EnqueueWaveHeader,
|
||||
Header);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
EnqueueWaveHeader
|
||||
Put the header in the record/playback queue. This is performed within
|
||||
the context of the sound thread, it must NEVER be called from another
|
||||
thread.
|
||||
|
||||
CompleteWaveHeader
|
||||
Set the header information to indicate that it has finished playing,
|
||||
and return it to the client application. This again must be called
|
||||
within the context of the sound thread.
|
||||
*/
|
||||
|
||||
MMRESULT
|
||||
EnqueueWaveHeader(
|
||||
PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
|
||||
IN PVOID Parameter)
|
||||
{
|
||||
PWAVEHDR WaveHeader = (PWAVEHDR) Parameter;
|
||||
|
||||
VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance );
|
||||
VALIDATE_MMSYS_PARAMETER( Parameter );
|
||||
|
||||
/* Initialise */
|
||||
WaveHeader->lpNext = NULL;
|
||||
|
||||
/* Set the "in queue" flag */
|
||||
WaveHeader->dwFlags |= WHDR_INQUEUE;
|
||||
|
||||
if ( ! SoundDeviceInstance->HeadWaveHeader )
|
||||
{
|
||||
/* This is the first header in the queue */
|
||||
SND_TRACE(L"Enqueued first wave header\n");
|
||||
SoundDeviceInstance->HeadWaveHeader = WaveHeader;
|
||||
SoundDeviceInstance->TailWaveHeader = WaveHeader;
|
||||
|
||||
DoWaveStreaming(SoundDeviceInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* There are already queued headers - make this one the tail */
|
||||
SND_TRACE(L"Enqueued next wave header\n");
|
||||
|
||||
/* FIXME - Make sure that the buffer has not already been added to the list */
|
||||
if ( SoundDeviceInstance->TailWaveHeader != WaveHeader )
|
||||
{
|
||||
SND_ASSERT(SoundDeviceInstance->TailWaveHeader != WaveHeader);
|
||||
|
||||
SoundDeviceInstance->TailWaveHeader->lpNext = WaveHeader;
|
||||
SoundDeviceInstance->TailWaveHeader = WaveHeader;
|
||||
DUMP_WAVEHDR_QUEUE(SoundDeviceInstance);
|
||||
|
||||
DoWaveStreaming(SoundDeviceInstance);
|
||||
}
|
||||
}
|
||||
|
||||
DUMP_WAVEHDR_QUEUE(SoundDeviceInstance);
|
||||
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
VOID
|
||||
CompleteWaveHeader(
|
||||
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
|
||||
IN PWAVEHDR Header)
|
||||
{
|
||||
PWAVEHDR PrevHdr = NULL, CurrHdr = NULL;
|
||||
PWAVEHDR_EXTENSION Extension;
|
||||
PSOUND_DEVICE SoundDevice;
|
||||
MMDEVICE_TYPE DeviceType;
|
||||
MMRESULT Result;
|
||||
|
||||
SND_TRACE(L"BUFFER COMPLETE :)\n");
|
||||
|
||||
// TODO: Set header flags?
|
||||
// TODO: Call client
|
||||
// TODO: Streaming
|
||||
|
||||
//DoWaveStreaming(SoundDeviceInstance);
|
||||
|
||||
Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
|
||||
SND_ASSERT( MMSUCCESS(Result) );
|
||||
Result = GetSoundDeviceType(SoundDevice, &DeviceType);
|
||||
SND_ASSERT( MMSUCCESS(Result) );
|
||||
|
||||
Extension = (PWAVEHDR_EXTENSION)Header->reserved;
|
||||
SND_ASSERT( Extension );
|
||||
|
||||
/* Remove the header from the queue, like so */
|
||||
if ( SoundDeviceInstance->HeadWaveHeader == Header )
|
||||
{
|
||||
SoundDeviceInstance->HeadWaveHeader = Header->lpNext;
|
||||
|
||||
SND_TRACE(L"Dropping head node\n");
|
||||
|
||||
/* If nothing after the head, then there is no tail */
|
||||
if ( Header->lpNext == NULL )
|
||||
{
|
||||
SND_TRACE(L"Dropping tail node\n");
|
||||
SoundDeviceInstance->TailWaveHeader = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PrevHdr = NULL;
|
||||
CurrHdr = SoundDeviceInstance->HeadWaveHeader;
|
||||
|
||||
SND_TRACE(L"Relinking nodes\n");
|
||||
|
||||
while ( CurrHdr != Header )
|
||||
{
|
||||
PrevHdr = CurrHdr;
|
||||
CurrHdr = CurrHdr->lpNext;
|
||||
SND_ASSERT( CurrHdr );
|
||||
}
|
||||
|
||||
SND_ASSERT( PrevHdr );
|
||||
|
||||
PrevHdr->lpNext = CurrHdr->lpNext;
|
||||
|
||||
/* If this is the tail node, update the tail */
|
||||
if ( Header->lpNext == NULL )
|
||||
{
|
||||
SND_TRACE(L"Updating tail node\n");
|
||||
SoundDeviceInstance->TailWaveHeader = PrevHdr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure we're not using this as the current buffer any more, either! */
|
||||
/*
|
||||
if ( SoundDeviceInstance->CurrentWaveHeader == Header )
|
||||
{
|
||||
SoundDeviceInstance->CurrentWaveHeader = Header->lpNext;
|
||||
}
|
||||
*/
|
||||
|
||||
DUMP_WAVEHDR_QUEUE(SoundDeviceInstance);
|
||||
|
||||
SND_TRACE(L"Returning buffer to client...\n");
|
||||
|
||||
/* Update the header */
|
||||
Header->dwFlags &= ~WHDR_INQUEUE;
|
||||
Header->dwFlags |= WHDR_DONE;
|
||||
|
||||
if ( DeviceType == WAVE_IN_DEVICE_TYPE )
|
||||
{
|
||||
// FIXME: We won't be called on incomplete buffer!
|
||||
Header->dwBytesRecorded = Extension->BytesCompleted;
|
||||
}
|
||||
|
||||
/* Safe to do this without thread protection, as we're done with the header */
|
||||
NotifyMmeClient(SoundDeviceInstance,
|
||||
DeviceType == WAVE_OUT_DEVICE_TYPE ? WOM_DONE : WIM_DATA,
|
||||
(DWORD_PTR)Header);
|
||||
}
|
309
lib/drivers/sound/mmebuddy/wave/streaming.c
Normal file
309
lib/drivers/sound/mmebuddy/wave/streaming.c
Normal file
|
@ -0,0 +1,309 @@
|
|||
/*
|
||||
* PROJECT: ReactOS Sound System "MME Buddy" Library
|
||||
* LICENSE: GPL - See COPYING in the top level directory
|
||||
* FILE: lib/drivers/sound/mmebuddy/wave/streaming.c
|
||||
*
|
||||
* PURPOSE: Wave streaming
|
||||
*
|
||||
* PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#include <mmddk.h>
|
||||
#include <ntddsnd.h>
|
||||
#include <mmebuddy.h>
|
||||
#include <sndtypes.h>
|
||||
|
||||
|
||||
/*
|
||||
DoWaveStreaming
|
||||
Check if there is streaming to be done, and if so, do it.
|
||||
*/
|
||||
|
||||
VOID
|
||||
DoWaveStreaming(
|
||||
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
|
||||
{
|
||||
MMRESULT Result;
|
||||
MMDEVICE_TYPE DeviceType;
|
||||
PSOUND_DEVICE SoundDevice;
|
||||
PMMFUNCTION_TABLE FunctionTable;
|
||||
PWAVEHDR Header;
|
||||
PWAVEHDR_EXTENSION HeaderExtension;
|
||||
|
||||
Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
|
||||
SND_ASSERT( MMSUCCESS(Result) );
|
||||
|
||||
Result = GetSoundDeviceType(SoundDevice, &DeviceType);
|
||||
SND_ASSERT( MMSUCCESS(Result) );
|
||||
|
||||
Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
|
||||
SND_ASSERT( MMSUCCESS(Result) );
|
||||
SND_ASSERT( FunctionTable );
|
||||
SND_ASSERT( FunctionTable->CommitWaveBuffer );
|
||||
|
||||
/* No point in doing anything if no resources available to use */
|
||||
if ( SoundDeviceInstance->OutstandingBuffers >= SoundDeviceInstance->BufferCount )
|
||||
{
|
||||
SND_TRACE(L"DoWaveStreaming: No available buffers to stream with - doing nothing\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Is there any work to do? */
|
||||
Header = SoundDeviceInstance->HeadWaveHeader;
|
||||
|
||||
if ( ! Header )
|
||||
{
|
||||
SND_TRACE(L"DoWaveStreaming: No work to do - doing nothing\n");
|
||||
return;
|
||||
}
|
||||
|
||||
while ( ( SoundDeviceInstance->OutstandingBuffers < SoundDeviceInstance->BufferCount ) &&
|
||||
( Header ) )
|
||||
{
|
||||
HeaderExtension = (PWAVEHDR_EXTENSION) Header->reserved;
|
||||
SND_ASSERT( HeaderExtension );
|
||||
|
||||
/* Saniy checks */
|
||||
SND_ASSERT(Header->dwFlags & WHDR_PREPARED);
|
||||
SND_ASSERT(Header->dwFlags & WHDR_INQUEUE);
|
||||
|
||||
/* Can never be *above* the length */
|
||||
SND_ASSERT( HeaderExtension->BytesCommitted <= Header->dwBufferLength );
|
||||
|
||||
/* Is this header entirely committed? */
|
||||
if ( HeaderExtension->BytesCommitted == Header->dwBufferLength )
|
||||
{
|
||||
{
|
||||
/* Move on to the next header */
|
||||
SND_ASSERT(Header != Header->lpNext);
|
||||
Header = Header->lpNext;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PSOUND_OVERLAPPED Overlap;
|
||||
LPVOID OffsetPtr;
|
||||
DWORD BytesRemaining, BytesToCommit;
|
||||
BOOL OK;
|
||||
|
||||
/* Where within the header buffer to stream from */
|
||||
OffsetPtr = Header->lpData + HeaderExtension->BytesCommitted;
|
||||
|
||||
/* How much of this header has not been committed */
|
||||
BytesRemaining = Header->dwBufferLength - HeaderExtension->BytesCommitted;
|
||||
|
||||
/* We can commit anything up to the buffer size limit */
|
||||
BytesToCommit = BytesRemaining > SoundDeviceInstance->FrameSize ?
|
||||
SoundDeviceInstance->FrameSize :
|
||||
BytesRemaining;
|
||||
|
||||
/* Should always have something to commit by this point */
|
||||
SND_ASSERT( BytesToCommit > 0 );
|
||||
|
||||
/* We need a new overlapped info structure for each buffer */
|
||||
Overlap = AllocateStruct(SOUND_OVERLAPPED);
|
||||
|
||||
if ( Overlap )
|
||||
{
|
||||
ZeroMemory(Overlap, sizeof(SOUND_OVERLAPPED));
|
||||
Overlap->SoundDeviceInstance = SoundDeviceInstance;
|
||||
Overlap->Header = Header;
|
||||
|
||||
/* Don't complete this header if it's part of a loop */
|
||||
Overlap->PerformCompletion = TRUE;
|
||||
// ( SoundDeviceInstance->LoopsRemaining > 0 );
|
||||
|
||||
/* Adjust the commit-related counters */
|
||||
HeaderExtension->BytesCommitted += BytesToCommit;
|
||||
++ SoundDeviceInstance->OutstandingBuffers;
|
||||
|
||||
OK = MMSUCCESS(FunctionTable->CommitWaveBuffer(SoundDeviceInstance,
|
||||
OffsetPtr,
|
||||
BytesToCommit,
|
||||
Overlap,
|
||||
CompleteIO));
|
||||
|
||||
if ( ! OK )
|
||||
{
|
||||
/* Clean-up and try again on the next iteration (is this OK?) */
|
||||
SND_WARN(L"FAILED\n");
|
||||
|
||||
FreeMemory(Overlap);
|
||||
HeaderExtension->BytesCommitted -= BytesToCommit;
|
||||
-- SoundDeviceInstance->OutstandingBuffers;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
CompleteIO
|
||||
An APC called as a result of a call to CommitWaveHeaderToKernelDevice.
|
||||
This will count up the number of bytes which have been dealt with,
|
||||
and when the entire wave header has been dealt with, will call
|
||||
CompleteWaveHeader to have the wave header returned to the client.
|
||||
|
||||
CommitWaveHeaderToKernelDevice
|
||||
Sends portions of the buffer described by the wave header to a kernel
|
||||
device. This must only be called from within the context of the sound
|
||||
thread. The caller supplies either their own commit routine, or uses
|
||||
WriteFileEx_Committer. The committer is called with portions of the
|
||||
buffer specified in the wave header.
|
||||
|
||||
WriteFileEx_Committer
|
||||
Commit buffers using the WriteFileEx API.
|
||||
*/
|
||||
|
||||
VOID CALLBACK
|
||||
CompleteIO(
|
||||
IN DWORD dwErrorCode,
|
||||
IN DWORD dwNumberOfBytesTransferred,
|
||||
IN LPOVERLAPPED lpOverlapped)
|
||||
{
|
||||
MMDEVICE_TYPE DeviceType;
|
||||
PSOUND_DEVICE SoundDevice;
|
||||
PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
|
||||
PSOUND_OVERLAPPED SoundOverlapped = (PSOUND_OVERLAPPED) lpOverlapped;
|
||||
PWAVEHDR WaveHdr;
|
||||
PWAVEHDR_EXTENSION HdrExtension;
|
||||
MMRESULT Result;
|
||||
DWORD Bytes;
|
||||
|
||||
WaveHdr = (PWAVEHDR) SoundOverlapped->Header;
|
||||
SND_ASSERT( WaveHdr );
|
||||
|
||||
SND_ASSERT( ERROR_SUCCESS == dwErrorCode );
|
||||
|
||||
HdrExtension = (PWAVEHDR_EXTENSION) WaveHdr->reserved;
|
||||
SND_ASSERT( HdrExtension );
|
||||
|
||||
SoundDeviceInstance = SoundOverlapped->SoundDeviceInstance;
|
||||
|
||||
Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
|
||||
SND_ASSERT( MMSUCCESS(Result) );
|
||||
|
||||
Result = GetSoundDeviceType(SoundDevice, &DeviceType);
|
||||
SND_ASSERT( MMSUCCESS(Result) );
|
||||
|
||||
do
|
||||
{
|
||||
|
||||
/* We have an available buffer now */
|
||||
-- SoundDeviceInstance->OutstandingBuffers;
|
||||
|
||||
/* Did we finish a WAVEHDR and aren't looping? */
|
||||
if ( HdrExtension->BytesCompleted + dwNumberOfBytesTransferred >= WaveHdr->dwBufferLength &&
|
||||
SoundOverlapped->PerformCompletion )
|
||||
{
|
||||
/* Wave buffer fully completed */
|
||||
Bytes = WaveHdr->dwBufferLength - HdrExtension->BytesCompleted;
|
||||
|
||||
HdrExtension->BytesCompleted += Bytes;
|
||||
dwNumberOfBytesTransferred -= Bytes;
|
||||
|
||||
CompleteWaveHeader(SoundDeviceInstance, WaveHdr);
|
||||
SND_TRACE(L"%d/%d bytes of wavehdr completed\n", HdrExtension->BytesCompleted, WaveHdr->dwBufferLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Partially completed */
|
||||
HdrExtension->BytesCompleted += dwNumberOfBytesTransferred;
|
||||
SND_TRACE(L"%d/%d bytes of wavehdr completed\n", HdrExtension->BytesCompleted, WaveHdr->dwBufferLength);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Move to next wave header */
|
||||
WaveHdr = WaveHdr->lpNext;
|
||||
|
||||
if (!WaveHdr)
|
||||
{
|
||||
/* No following WaveHdr */
|
||||
SND_ASSERT(dwNumberOfBytesTransferred == 0);
|
||||
break;
|
||||
}
|
||||
|
||||
HdrExtension = (PWAVEHDR_EXTENSION) WaveHdr->reserved;
|
||||
SND_ASSERT( HdrExtension );
|
||||
|
||||
|
||||
}while(dwNumberOfBytesTransferred);
|
||||
|
||||
DoWaveStreaming(SoundDeviceInstance);
|
||||
|
||||
//CompleteWavePortion(SoundDeviceInstance, dwNumberOfBytesTransferred);
|
||||
|
||||
FreeMemory(lpOverlapped);
|
||||
}
|
||||
|
||||
MMRESULT
|
||||
WriteFileEx_Committer(
|
||||
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
|
||||
IN PVOID OffsetPtr,
|
||||
IN DWORD Length,
|
||||
IN PSOUND_OVERLAPPED Overlap,
|
||||
IN LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine)
|
||||
{
|
||||
HANDLE Handle;
|
||||
|
||||
VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance );
|
||||
VALIDATE_MMSYS_PARAMETER( OffsetPtr );
|
||||
VALIDATE_MMSYS_PARAMETER( Overlap );
|
||||
VALIDATE_MMSYS_PARAMETER( CompletionRoutine );
|
||||
|
||||
GetSoundDeviceInstanceHandle(SoundDeviceInstance, &Handle);
|
||||
|
||||
if ( ! WriteFileEx(Handle, OffsetPtr, Length, (LPOVERLAPPED)Overlap, CompletionRoutine) )
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Stream control functions
|
||||
(External/internal thread pairs)
|
||||
|
||||
TODO - Move elsewhere as these shouldn't be wave specific!
|
||||
*/
|
||||
|
||||
MMRESULT
|
||||
StopStreamingInSoundThread(
|
||||
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
|
||||
IN PVOID Parameter)
|
||||
{
|
||||
/* TODO */
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
MMRESULT
|
||||
StopStreaming(
|
||||
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
|
||||
{
|
||||
MMRESULT Result;
|
||||
PSOUND_DEVICE SoundDevice;
|
||||
MMDEVICE_TYPE DeviceType;
|
||||
|
||||
if ( ! IsValidSoundDeviceInstance(SoundDeviceInstance) )
|
||||
return MMSYSERR_INVALHANDLE;
|
||||
|
||||
Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
|
||||
if ( ! MMSUCCESS(Result) )
|
||||
return TranslateInternalMmResult(Result);
|
||||
|
||||
Result = GetSoundDeviceType(SoundDevice, &DeviceType);
|
||||
if ( ! MMSUCCESS(Result) )
|
||||
return TranslateInternalMmResult(Result);
|
||||
|
||||
if ( DeviceType != WAVE_OUT_DEVICE_TYPE && DeviceType != WAVE_IN_DEVICE_TYPE )
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
|
||||
return CallSoundThread(SoundDeviceInstance,
|
||||
StopStreamingInSoundThread,
|
||||
NULL);
|
||||
}
|
132
lib/drivers/sound/mmebuddy/wave/widMessage.c
Normal file
132
lib/drivers/sound/mmebuddy/wave/widMessage.c
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* 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 <sndtypes.h>
|
||||
|
||||
#include <mmebuddy.h>
|
||||
|
||||
/*
|
||||
Standard MME driver entry-point for messages relating to wave audio
|
||||
input.
|
||||
*/
|
||||
DWORD
|
||||
APIENTRY
|
||||
widMessage(
|
||||
UINT DeviceId,
|
||||
UINT Message,
|
||||
DWORD_PTR PrivateHandle,
|
||||
DWORD_PTR Parameter1,
|
||||
DWORD_PTR 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_START :
|
||||
{
|
||||
Result = MmeSetState(PrivateHandle, TRUE);
|
||||
break;
|
||||
}
|
||||
|
||||
case WIDM_STOP :
|
||||
{
|
||||
Result = MmeSetState(PrivateHandle, FALSE);
|
||||
break;
|
||||
}
|
||||
|
||||
case WIDM_GETDEVCAPS :
|
||||
{
|
||||
|
||||
Result = MmeGetSoundDeviceCapabilities(WAVE_IN_DEVICE_TYPE,
|
||||
DeviceId,
|
||||
(PVOID) Parameter1,
|
||||
Parameter2);
|
||||
break;
|
||||
}
|
||||
case WIDM_OPEN :
|
||||
{
|
||||
Result = MmeOpenWaveDevice(WAVE_IN_DEVICE_TYPE,
|
||||
DeviceId,
|
||||
(LPWAVEOPENDESC) Parameter1,
|
||||
Parameter2,
|
||||
(DWORD_PTR*) PrivateHandle);
|
||||
break;
|
||||
}
|
||||
|
||||
case WIDM_CLOSE :
|
||||
{
|
||||
Result = MmeCloseDevice(PrivateHandle);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case WIDM_PREPARE :
|
||||
{
|
||||
/* TODO: Do we need to pass 2nd parameter? */
|
||||
Result = MmePrepareWaveHeader(PrivateHandle, Parameter1);
|
||||
break;
|
||||
}
|
||||
|
||||
case WIDM_UNPREPARE :
|
||||
{
|
||||
Result = MmeUnprepareWaveHeader(PrivateHandle, Parameter1);
|
||||
break;
|
||||
}
|
||||
|
||||
case WIDM_RESET :
|
||||
{
|
||||
/* Stop playback, reset position to zero */
|
||||
Result = MmeResetWavePlayback(PrivateHandle);
|
||||
break;
|
||||
}
|
||||
|
||||
case WIDM_ADDBUFFER :
|
||||
{
|
||||
Result = MmeWriteWaveHeader(PrivateHandle, Parameter1);
|
||||
break;
|
||||
}
|
||||
|
||||
case DRV_QUERYDEVICEINTERFACESIZE :
|
||||
{
|
||||
Result = MmeGetDeviceInterfaceString(WAVE_IN_DEVICE_TYPE, DeviceId, NULL, 0, (DWORD*)Parameter1); //FIXME DWORD_PTR
|
||||
break;
|
||||
}
|
||||
|
||||
case DRV_QUERYDEVICEINTERFACE :
|
||||
{
|
||||
Result = MmeGetDeviceInterfaceString(WAVE_IN_DEVICE_TYPE, DeviceId, (LPWSTR)Parameter1, Parameter2, NULL); //FIXME DWORD_PTR
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
SND_TRACE(L"widMessage returning MMRESULT %d\n", Result);
|
||||
|
||||
ReleaseEntrypointMutex(WAVE_IN_DEVICE_TYPE);
|
||||
|
||||
return Result;
|
||||
}
|
139
lib/drivers/sound/mmebuddy/wave/wodMessage.c
Normal file
139
lib/drivers/sound/mmebuddy/wave/wodMessage.c
Normal file
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* 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 <sndtypes.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.
|
||||
*/
|
||||
DWORD
|
||||
APIENTRY
|
||||
wodMessage(
|
||||
UINT DeviceId,
|
||||
UINT Message,
|
||||
DWORD_PTR PrivateHandle,
|
||||
DWORD_PTR Parameter1,
|
||||
DWORD_PTR 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_PTR*)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 = MmeWriteWaveHeader(PrivateHandle, Parameter1);
|
||||
break;
|
||||
}
|
||||
|
||||
case WODM_RESET :
|
||||
{
|
||||
/* Stop playback, reset position to zero */
|
||||
Result = MmeResetWavePlayback(PrivateHandle);
|
||||
break;
|
||||
}
|
||||
|
||||
case WODM_RESTART :
|
||||
{
|
||||
/* Continue playback when paused */
|
||||
break;
|
||||
}
|
||||
|
||||
case WODM_GETPOS :
|
||||
{
|
||||
Result = MmeGetPosition(WAVE_OUT_DEVICE_TYPE, DeviceId, PrivateHandle, (MMTIME*)Parameter1, Parameter2);
|
||||
break;
|
||||
}
|
||||
|
||||
case DRV_QUERYDEVICEINTERFACESIZE :
|
||||
{
|
||||
Result = MmeGetDeviceInterfaceString(WAVE_OUT_DEVICE_TYPE, DeviceId, NULL, 0, (DWORD*)Parameter1); //FIXME DWORD_PTR
|
||||
break;
|
||||
}
|
||||
|
||||
case DRV_QUERYDEVICEINTERFACE :
|
||||
{
|
||||
Result = MmeGetDeviceInterfaceString(WAVE_OUT_DEVICE_TYPE, DeviceId, (LPWSTR)Parameter1, Parameter2, NULL); //FIXME DWORD_PTR
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SND_TRACE(L"wodMessage returning MMRESULT %d\n", Result);
|
||||
|
||||
ReleaseEntrypointMutex(WAVE_OUT_DEVICE_TYPE);
|
||||
|
||||
return Result;
|
||||
}
|
249
lib/drivers/sound/mment4/control.c
Normal file
249
lib/drivers/sound/mment4/control.c
Normal file
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#define NDEBUG
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#include <mmddk.h>
|
||||
#include <ntddsnd.h>
|
||||
#include <sndtypes.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 );
|
||||
IoCtl = 0;
|
||||
}
|
||||
|
||||
/* 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 DWORD DeviceId,
|
||||
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;
|
||||
}
|
||||
|
||||
#if 0
|
||||
MMRESULT
|
||||
SubmitNt4WaveHeader(
|
||||
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
|
||||
IN PWAVEHDR WaveHeader)
|
||||
{
|
||||
VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance );
|
||||
VALIDATE_MMSYS_PARAMETER( WaveHeader );
|
||||
|
||||
SND_TRACE(L"Submitting wave header %p (in sound thread)\n", WaveHeader);
|
||||
|
||||
/* TODO: This should only submit the header to the device, nothing more! */
|
||||
}
|
||||
#endif
|
207
lib/drivers/sound/mment4/detect.c
Normal file
207
lib/drivers/sound/mment4/detect.c
Normal file
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* 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 <sndnames.h>
|
||||
#include <sndtypes.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;
|
||||
}
|
9
lib/drivers/sound/mment4/mment4.rbuild
Normal file
9
lib/drivers/sound/mment4/mment4.rbuild
Normal file
|
@ -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>
|
140
lib/drivers/sound/mment4/registry.c
Normal file
140
lib/drivers/sound/mment4/registry.c
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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 <sndnames.h>
|
||||
#include <sndtypes.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;
|
||||
}
|
1165
lib/drivers/sound/mmixer/controls.c
Normal file
1165
lib/drivers/sound/mmixer/controls.c
Normal file
File diff suppressed because it is too large
Load diff
282
lib/drivers/sound/mmixer/filter.c
Normal file
282
lib/drivers/sound/mmixer/filter.c
Normal file
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* COPYRIGHT: See COPYING in the top level directory
|
||||
* PROJECT: ReactOS Kernel Streaming
|
||||
* FILE: lib/drivers/sound/mmixer/filter.c
|
||||
* PURPOSE: Mixer Filter Functions
|
||||
* PROGRAMMER: Johannes Anderwald
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "priv.h"
|
||||
|
||||
ULONG
|
||||
MMixerGetFilterPinCount(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN HANDLE hMixer)
|
||||
{
|
||||
KSPROPERTY Pin;
|
||||
MIXER_STATUS Status;
|
||||
ULONG NumPins, BytesReturned;
|
||||
|
||||
// setup property request
|
||||
Pin.Flags = KSPROPERTY_TYPE_GET;
|
||||
Pin.Set = KSPROPSETID_Pin;
|
||||
Pin.Id = KSPROPERTY_PIN_CTYPES;
|
||||
|
||||
// query pin count
|
||||
Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSPROPERTY), (PVOID)&NumPins, sizeof(ULONG), (PULONG)&BytesReturned);
|
||||
|
||||
// check for success
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
return 0;
|
||||
|
||||
return NumPins;
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetFilterTopologyProperty(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN HANDLE hMixer,
|
||||
IN ULONG PropertyId,
|
||||
OUT PKSMULTIPLE_ITEM * OutMultipleItem)
|
||||
{
|
||||
KSPROPERTY Property;
|
||||
PKSMULTIPLE_ITEM MultipleItem;
|
||||
MIXER_STATUS Status;
|
||||
ULONG BytesReturned;
|
||||
|
||||
// setup property request
|
||||
Property.Id = PropertyId;
|
||||
Property.Flags = KSPROPERTY_TYPE_GET;
|
||||
Property.Set = KSPROPSETID_Topology;
|
||||
|
||||
// query for the size
|
||||
Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSPROPERTY), NULL, 0, &BytesReturned);
|
||||
|
||||
if (Status != MM_STATUS_MORE_ENTRIES)
|
||||
return Status;
|
||||
|
||||
//sanity check
|
||||
ASSERT(BytesReturned);
|
||||
|
||||
// allocate an result buffer
|
||||
MultipleItem = (PKSMULTIPLE_ITEM)MixerContext->Alloc(BytesReturned);
|
||||
|
||||
if (!MultipleItem)
|
||||
{
|
||||
// not enough memory
|
||||
return MM_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
// query again with allocated buffer
|
||||
Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSPROPERTY), (PVOID)MultipleItem, BytesReturned, &BytesReturned);
|
||||
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
// failed
|
||||
MixerContext->Free((PVOID)MultipleItem);
|
||||
return Status;
|
||||
}
|
||||
|
||||
// store result
|
||||
*OutMultipleItem = MultipleItem;
|
||||
|
||||
// done
|
||||
return Status;
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetPhysicalConnection(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN HANDLE hMixer,
|
||||
IN ULONG PinId,
|
||||
OUT PKSPIN_PHYSICALCONNECTION *OutConnection)
|
||||
{
|
||||
KSP_PIN Pin;
|
||||
MIXER_STATUS Status;
|
||||
ULONG BytesReturned;
|
||||
PKSPIN_PHYSICALCONNECTION Connection;
|
||||
|
||||
/* setup the request */
|
||||
Pin.Property.Flags = KSPROPERTY_TYPE_GET;
|
||||
Pin.Property.Id = KSPROPERTY_PIN_PHYSICALCONNECTION;
|
||||
Pin.Property.Set = KSPROPSETID_Pin;
|
||||
Pin.PinId = PinId;
|
||||
|
||||
/* query the pin for the physical connection */
|
||||
Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), NULL, 0, &BytesReturned);
|
||||
|
||||
if (Status == MM_STATUS_UNSUCCESSFUL)
|
||||
{
|
||||
// pin does not have a physical connection
|
||||
return Status;
|
||||
}
|
||||
DPRINT("Status %u BytesReturned %lu\n", Status, BytesReturned);
|
||||
Connection = (PKSPIN_PHYSICALCONNECTION)MixerContext->Alloc(BytesReturned);
|
||||
if (!Connection)
|
||||
{
|
||||
// not enough memory
|
||||
return MM_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
// query the pin for the physical connection
|
||||
Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), (PVOID)Connection, BytesReturned, &BytesReturned);
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
// failed to query the physical connection
|
||||
MixerContext->Free(Connection);
|
||||
DPRINT("Status %u\n", Status);
|
||||
return Status;
|
||||
}
|
||||
|
||||
// store connection
|
||||
*OutConnection = Connection;
|
||||
return Status;
|
||||
}
|
||||
|
||||
ULONG
|
||||
MMixerGetControlTypeFromTopologyNode(
|
||||
IN LPGUID NodeType)
|
||||
{
|
||||
if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_AGC))
|
||||
{
|
||||
// automatic gain control
|
||||
return MIXERCONTROL_CONTROLTYPE_ONOFF;
|
||||
}
|
||||
else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_LOUDNESS))
|
||||
{
|
||||
// loudness control
|
||||
return MIXERCONTROL_CONTROLTYPE_LOUDNESS;
|
||||
}
|
||||
else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_MUTE ))
|
||||
{
|
||||
// mute control
|
||||
return MIXERCONTROL_CONTROLTYPE_MUTE;
|
||||
}
|
||||
else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_TONE))
|
||||
{
|
||||
// tpne control
|
||||
//FIXME
|
||||
// MIXERCONTROL_CONTROLTYPE_ONOFF if KSPROPERTY_AUDIO_BASS_BOOST is supported
|
||||
// MIXERCONTROL_CONTROLTYPE_BASS if KSPROPERTY_AUDIO_BASS is supported
|
||||
// MIXERCONTROL_CONTROLTYPE_TREBLE if KSPROPERTY_AUDIO_TREBLE is supported
|
||||
UNIMPLEMENTED;
|
||||
return MIXERCONTROL_CONTROLTYPE_ONOFF;
|
||||
}
|
||||
else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_VOLUME))
|
||||
{
|
||||
// volume control
|
||||
return MIXERCONTROL_CONTROLTYPE_VOLUME;
|
||||
}
|
||||
else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_PEAKMETER))
|
||||
{
|
||||
// peakmeter control
|
||||
return MIXERCONTROL_CONTROLTYPE_PEAKMETER;
|
||||
}
|
||||
else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_MUX))
|
||||
{
|
||||
// mux control
|
||||
return MIXERCONTROL_CONTROLTYPE_MUX;
|
||||
}
|
||||
else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_MUX))
|
||||
{
|
||||
// mux control
|
||||
return MIXERCONTROL_CONTROLTYPE_MUX;
|
||||
}
|
||||
else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_STEREO_WIDE))
|
||||
{
|
||||
// stero wide control
|
||||
return MIXERCONTROL_CONTROLTYPE_FADER;
|
||||
}
|
||||
else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_CHORUS))
|
||||
{
|
||||
// chorus control
|
||||
return MIXERCONTROL_CONTROLTYPE_FADER;
|
||||
}
|
||||
else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_REVERB))
|
||||
{
|
||||
// reverb control
|
||||
return MIXERCONTROL_CONTROLTYPE_FADER;
|
||||
}
|
||||
else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_SUPERMIX))
|
||||
{
|
||||
// supermix control
|
||||
// MIXERCONTROL_CONTROLTYPE_MUTE if KSPROPERTY_AUDIO_MUTE is supported
|
||||
UNIMPLEMENTED;
|
||||
return MIXERCONTROL_CONTROLTYPE_VOLUME;
|
||||
}
|
||||
//TODO
|
||||
//check for other supported node types
|
||||
//UNIMPLEMENTED
|
||||
return 0;
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerSetGetControlDetails(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN HANDLE hMixer,
|
||||
IN ULONG NodeId,
|
||||
IN ULONG bSet,
|
||||
IN ULONG PropertyId,
|
||||
IN ULONG Channel,
|
||||
IN PLONG InputValue)
|
||||
{
|
||||
KSNODEPROPERTY_AUDIO_CHANNEL Property;
|
||||
MIXER_STATUS Status;
|
||||
LONG Value;
|
||||
ULONG BytesReturned;
|
||||
|
||||
if (bSet)
|
||||
Value = *InputValue;
|
||||
|
||||
/* setup the request */
|
||||
RtlZeroMemory(&Property, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL));
|
||||
|
||||
Property.NodeProperty.NodeId = NodeId;
|
||||
Property.NodeProperty.Property.Id = PropertyId;
|
||||
Property.NodeProperty.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY;
|
||||
Property.NodeProperty.Property.Set = KSPROPSETID_Audio;
|
||||
Property.Channel = Channel;
|
||||
Property.Reserved = 0;
|
||||
|
||||
if (bSet)
|
||||
Property.NodeProperty.Property.Flags |= KSPROPERTY_TYPE_SET;
|
||||
else
|
||||
Property.NodeProperty.Property.Flags |= KSPROPERTY_TYPE_GET;
|
||||
|
||||
/* send the request */
|
||||
Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL), (PVOID)&Value, sizeof(LONG), &BytesReturned);
|
||||
|
||||
if (!bSet && Status == MM_STATUS_SUCCESS)
|
||||
{
|
||||
*InputValue = Value;
|
||||
}
|
||||
|
||||
DPRINT("Status %x bSet %u NodeId %u Value %d PropertyId %u\n", Status, bSet, NodeId, Value, PropertyId);
|
||||
return Status;
|
||||
}
|
||||
|
||||
ULONG
|
||||
MMixerGetPinInstanceCount(
|
||||
PMIXER_CONTEXT MixerContext,
|
||||
HANDLE hFilter,
|
||||
ULONG PinId)
|
||||
{
|
||||
KSP_PIN PinRequest;
|
||||
KSPIN_CINSTANCES PinInstances;
|
||||
ULONG BytesReturned;
|
||||
MIXER_STATUS Status;
|
||||
|
||||
/* query the instance count */
|
||||
PinRequest.Reserved = 0;
|
||||
PinRequest.PinId = PinId;
|
||||
PinRequest.Property.Set = KSPROPSETID_Pin;
|
||||
PinRequest.Property.Flags = KSPROPERTY_TYPE_GET;
|
||||
PinRequest.Property.Id = KSPROPERTY_PIN_CINSTANCES;
|
||||
|
||||
Status = MixerContext->Control(hFilter, IOCTL_KS_PROPERTY, (PVOID)&PinRequest, sizeof(KSP_PIN), (PVOID)&PinInstances, sizeof(KSPIN_CINSTANCES), &BytesReturned);
|
||||
ASSERT(Status == MM_STATUS_SUCCESS);
|
||||
return PinInstances.CurrentCount;
|
||||
}
|
||||
|
509
lib/drivers/sound/mmixer/mixer.c
Normal file
509
lib/drivers/sound/mmixer/mixer.c
Normal file
|
@ -0,0 +1,509 @@
|
|||
/*
|
||||
* COPYRIGHT: See COPYING in the top level directory
|
||||
* PROJECT: ReactOS Kernel Streaming
|
||||
* FILE: lib/drivers/sound/mmixer/mmixer.c
|
||||
* PURPOSE: Mixer Handling Functions
|
||||
* PROGRAMMER: Johannes Anderwald
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "priv.h"
|
||||
|
||||
ULONG
|
||||
MMixerGetCount(
|
||||
IN PMIXER_CONTEXT MixerContext)
|
||||
{
|
||||
PMIXER_LIST MixerList;
|
||||
MIXER_STATUS Status;
|
||||
|
||||
// verify mixer context
|
||||
Status = MMixerVerifyContext(MixerContext);
|
||||
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
// invalid context passed
|
||||
return Status;
|
||||
}
|
||||
|
||||
// grab mixer list
|
||||
MixerList = (PMIXER_LIST)MixerContext->MixerContext;
|
||||
|
||||
// return number of mixers
|
||||
return MixerList->MixerListCount;
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetCapabilities(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN ULONG MixerIndex,
|
||||
OUT LPMIXERCAPSW MixerCaps)
|
||||
{
|
||||
MIXER_STATUS Status;
|
||||
LPMIXER_INFO MixerInfo;
|
||||
|
||||
// verify mixer context
|
||||
Status = MMixerVerifyContext(MixerContext);
|
||||
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
// invalid context passed
|
||||
return Status;
|
||||
}
|
||||
|
||||
// get mixer info
|
||||
MixerInfo = MMixerGetMixerInfoByIndex(MixerContext, MixerIndex);
|
||||
|
||||
if (!MixerInfo)
|
||||
{
|
||||
// invalid device index
|
||||
return MM_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
MixerCaps->wMid = MixerInfo->MixCaps.wMid;
|
||||
MixerCaps->wPid = MixerInfo->MixCaps.wPid;
|
||||
MixerCaps->vDriverVersion = MixerInfo->MixCaps.vDriverVersion;
|
||||
MixerCaps->fdwSupport = MixerInfo->MixCaps.fdwSupport;
|
||||
MixerCaps->cDestinations = MixerInfo->MixCaps.cDestinations;
|
||||
|
||||
ASSERT(MixerInfo->MixCaps.szPname[MAXPNAMELEN-1] == 0);
|
||||
wcscpy(MixerCaps->szPname, MixerInfo->MixCaps.szPname);
|
||||
|
||||
return MM_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerOpen(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN ULONG MixerId,
|
||||
IN PVOID MixerEvent,
|
||||
IN PMIXER_EVENT MixerEventRoutine,
|
||||
OUT PHANDLE MixerHandle)
|
||||
{
|
||||
MIXER_STATUS Status;
|
||||
LPMIXER_INFO MixerInfo;
|
||||
|
||||
// verify mixer context
|
||||
Status = MMixerVerifyContext(MixerContext);
|
||||
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
// invalid context passed
|
||||
return Status;
|
||||
}
|
||||
|
||||
MixerInfo = (LPMIXER_INFO)MMixerGetMixerInfoByIndex(MixerContext, MixerId);
|
||||
if (!MixerInfo)
|
||||
{
|
||||
// invalid mixer id
|
||||
return MM_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// FIXME
|
||||
// handle event notification
|
||||
|
||||
Status = MMixerAddEvents(MixerContext, MixerInfo);
|
||||
|
||||
|
||||
// store result
|
||||
*MixerHandle = (HANDLE)MixerInfo;
|
||||
|
||||
return MM_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetLineInfo(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN HANDLE MixerHandle,
|
||||
IN ULONG Flags,
|
||||
OUT LPMIXERLINEW MixerLine)
|
||||
{
|
||||
MIXER_STATUS Status;
|
||||
LPMIXER_INFO MixerInfo;
|
||||
LPMIXERLINE_EXT MixerLineSrc;
|
||||
|
||||
// verify mixer context
|
||||
Status = MMixerVerifyContext(MixerContext);
|
||||
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
// invalid context passed
|
||||
return Status;
|
||||
}
|
||||
|
||||
// clear hmixer from flags
|
||||
Flags &=~MIXER_OBJECTF_HMIXER;
|
||||
|
||||
if (Flags == MIXER_GETLINEINFOF_DESTINATION)
|
||||
{
|
||||
// cast to mixer info
|
||||
MixerInfo = (LPMIXER_INFO)MixerHandle;
|
||||
|
||||
if (MixerLine->dwDestination != 0)
|
||||
{
|
||||
// destination line member must be zero
|
||||
return MM_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, DESTINATION_LINE);
|
||||
ASSERT(MixerLineSrc);
|
||||
MixerContext->Copy(MixerLine, &MixerLineSrc->Line, sizeof(MIXERLINEW));
|
||||
|
||||
return MM_STATUS_SUCCESS;
|
||||
}
|
||||
else if (Flags == MIXER_GETLINEINFOF_SOURCE)
|
||||
{
|
||||
// cast to mixer info
|
||||
MixerInfo = (LPMIXER_INFO)MixerHandle;
|
||||
|
||||
|
||||
MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, DESTINATION_LINE);
|
||||
ASSERT(MixerLineSrc);
|
||||
|
||||
if (MixerLine->dwSource >= MixerLineSrc->Line.cConnections)
|
||||
{
|
||||
DPRINT1("dwSource %u > Destinations %u\n", MixerLine->dwSource, MixerLineSrc->Line.cConnections);
|
||||
|
||||
// invalid parameter
|
||||
return MM_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, MixerLine->dwSource * 0x10000);
|
||||
if (MixerLineSrc)
|
||||
{
|
||||
DPRINT("Line %u Name %S\n", MixerLineSrc->Line.dwSource, MixerLineSrc->Line.szName);
|
||||
MixerContext->Copy(MixerLine, &MixerLineSrc->Line, sizeof(MIXERLINEW));
|
||||
return MM_STATUS_SUCCESS;
|
||||
}
|
||||
return MM_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
else if (Flags == MIXER_GETLINEINFOF_LINEID)
|
||||
{
|
||||
// cast to mixer info
|
||||
MixerInfo = (LPMIXER_INFO)MixerHandle;
|
||||
|
||||
MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, MixerLine->dwLineID);
|
||||
if (!MixerLineSrc)
|
||||
{
|
||||
// invalid parameter
|
||||
return MM_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* copy cached data */
|
||||
MixerContext->Copy(MixerLine, &MixerLineSrc->Line, sizeof(MIXERLINEW));
|
||||
return MM_STATUS_SUCCESS;
|
||||
}
|
||||
else if (Flags == MIXER_GETLINEINFOF_COMPONENTTYPE)
|
||||
{
|
||||
// cast to mixer info
|
||||
MixerInfo = (LPMIXER_INFO)MixerHandle;
|
||||
|
||||
MixerLineSrc = MMixerGetSourceMixerLineByComponentType(MixerInfo, MixerLine->dwComponentType);
|
||||
if (!MixerLineSrc)
|
||||
{
|
||||
DPRINT1("Failed to find component type %x\n", MixerLine->dwComponentType);
|
||||
return MM_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
ASSERT(MixerLineSrc);
|
||||
|
||||
/* copy cached data */
|
||||
MixerContext->Copy(MixerLine, &MixerLineSrc->Line, sizeof(MIXERLINEW));
|
||||
return MM_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
return MM_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetLineControls(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN HANDLE MixerHandle,
|
||||
IN ULONG Flags,
|
||||
OUT LPMIXERLINECONTROLSW MixerLineControls)
|
||||
{
|
||||
LPMIXER_INFO MixerInfo;
|
||||
LPMIXERLINE_EXT MixerLineSrc;
|
||||
LPMIXERCONTROLW MixerControl;
|
||||
MIXER_STATUS Status;
|
||||
ULONG Index;
|
||||
|
||||
// verify mixer context
|
||||
Status = MMixerVerifyContext(MixerContext);
|
||||
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
// invalid context passed
|
||||
return Status;
|
||||
}
|
||||
|
||||
Flags &= ~MIXER_OBJECTF_HMIXER;
|
||||
|
||||
if (Flags == MIXER_GETLINECONTROLSF_ALL)
|
||||
{
|
||||
// cast to mixer info
|
||||
MixerInfo = (LPMIXER_INFO)MixerHandle;
|
||||
|
||||
MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, MixerLineControls->dwLineID);
|
||||
|
||||
if (!MixerLineSrc)
|
||||
{
|
||||
// invalid line id
|
||||
return MM_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
// copy line control(s)
|
||||
MixerContext->Copy(MixerLineControls->pamxctrl, MixerLineSrc->LineControls, min(MixerLineSrc->Line.cControls, MixerLineControls->cControls) * sizeof(MIXERCONTROLW));
|
||||
|
||||
return MM_STATUS_SUCCESS;
|
||||
}
|
||||
else if (Flags == MIXER_GETLINECONTROLSF_ONEBYTYPE)
|
||||
{
|
||||
// cast to mixer info
|
||||
MixerInfo = (LPMIXER_INFO)MixerHandle;
|
||||
|
||||
MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, MixerLineControls->dwLineID);
|
||||
|
||||
if (!MixerLineSrc)
|
||||
{
|
||||
// invalid line id
|
||||
return MM_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
ASSERT(MixerLineSrc);
|
||||
|
||||
Index = 0;
|
||||
for(Index = 0; Index < MixerLineSrc->Line.cControls; Index++)
|
||||
{
|
||||
DPRINT("dwControlType %x\n", MixerLineSrc->LineControls[Index].dwControlType);
|
||||
if (MixerLineControls->dwControlType == MixerLineSrc->LineControls[Index].dwControlType)
|
||||
{
|
||||
// found a control with that type
|
||||
MixerContext->Copy(MixerLineControls->pamxctrl, &MixerLineSrc->LineControls[Index], sizeof(MIXERCONTROLW));
|
||||
return MM_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
DPRINT("DeviceInfo->u.MixControls.dwControlType %x not found in Line %x cControls %u \n", MixerLineControls->dwControlType, MixerLineControls->dwLineID, MixerLineSrc->Line.cControls);
|
||||
return MM_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
else if (Flags == MIXER_GETLINECONTROLSF_ONEBYID)
|
||||
{
|
||||
// cast to mixer info
|
||||
MixerInfo = (LPMIXER_INFO)MixerHandle;
|
||||
|
||||
Status = MMixerGetMixerControlById(MixerInfo, MixerLineControls->dwControlID, NULL, &MixerControl, NULL);
|
||||
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
// invalid parameter
|
||||
return MM_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// copy the controls
|
||||
MixerContext->Copy(MixerLineControls->pamxctrl, MixerControl, sizeof(MIXERCONTROLW));
|
||||
return MM_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
return MM_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerSetControlDetails(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN HANDLE MixerHandle,
|
||||
IN ULONG Flags,
|
||||
OUT LPMIXERCONTROLDETAILS MixerControlDetails)
|
||||
{
|
||||
MIXER_STATUS Status;
|
||||
ULONG NodeId;
|
||||
LPMIXER_INFO MixerInfo;
|
||||
LPMIXERLINE_EXT MixerLine;
|
||||
LPMIXERCONTROLW MixerControl;
|
||||
|
||||
// verify mixer context
|
||||
Status = MMixerVerifyContext(MixerContext);
|
||||
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
// invalid context passed
|
||||
return Status;
|
||||
}
|
||||
|
||||
// get mixer info
|
||||
MixerInfo = (LPMIXER_INFO)MixerHandle;
|
||||
|
||||
// get mixer control
|
||||
Status = MMixerGetMixerControlById(MixerInfo, MixerControlDetails->dwControlID, &MixerLine, &MixerControl, &NodeId);
|
||||
|
||||
// check for success
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
// failed to find control id
|
||||
return MM_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
switch(MixerControl->dwControlType)
|
||||
{
|
||||
case MIXERCONTROL_CONTROLTYPE_MUTE:
|
||||
Status = MMixerSetGetMuteControlDetails(MixerContext, MixerInfo->hMixer, NodeId, MixerLine->Line.dwLineID, MixerControlDetails, TRUE);
|
||||
break;
|
||||
case MIXERCONTROL_CONTROLTYPE_VOLUME:
|
||||
Status = MMixerSetGetVolumeControlDetails(MixerContext, MixerInfo->hMixer, NodeId, TRUE, MixerControl, MixerControlDetails, MixerLine);
|
||||
break;
|
||||
default:
|
||||
Status = MM_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetControlDetails(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN HANDLE MixerHandle,
|
||||
IN ULONG Flags,
|
||||
OUT LPMIXERCONTROLDETAILS MixerControlDetails)
|
||||
{
|
||||
MIXER_STATUS Status;
|
||||
ULONG NodeId;
|
||||
LPMIXER_INFO MixerInfo;
|
||||
LPMIXERLINE_EXT MixerLine;
|
||||
LPMIXERCONTROLW MixerControl;
|
||||
|
||||
// verify mixer context
|
||||
Status = MMixerVerifyContext(MixerContext);
|
||||
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
// invalid context passed
|
||||
return Status;
|
||||
}
|
||||
|
||||
// get mixer info
|
||||
MixerInfo = (LPMIXER_INFO)MixerHandle;
|
||||
|
||||
// get mixer control
|
||||
Status = MMixerGetMixerControlById(MixerInfo, MixerControlDetails->dwControlID, &MixerLine, &MixerControl, &NodeId);
|
||||
|
||||
// check for success
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
// failed to find control id
|
||||
return MM_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
switch(MixerControl->dwControlType)
|
||||
{
|
||||
case MIXERCONTROL_CONTROLTYPE_MUTE:
|
||||
Status = MMixerSetGetMuteControlDetails(MixerContext, MixerInfo->hMixer, NodeId, MixerLine->Line.dwLineID, MixerControlDetails, FALSE);
|
||||
break;
|
||||
case MIXERCONTROL_CONTROLTYPE_VOLUME:
|
||||
Status = MMixerSetGetVolumeControlDetails(MixerContext, MixerInfo->hMixer, NodeId, FALSE, MixerControl, MixerControlDetails, MixerLine);
|
||||
break;
|
||||
default:
|
||||
Status = MM_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerInitialize(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN PMIXER_ENUM EnumFunction,
|
||||
IN PVOID EnumContext)
|
||||
{
|
||||
MIXER_STATUS Status;
|
||||
HANDLE hMixer, hKey;
|
||||
ULONG DeviceIndex, Count;
|
||||
LPWSTR DeviceName;
|
||||
LPMIXER_DATA MixerData;
|
||||
PMIXER_LIST MixerList;
|
||||
PLIST_ENTRY Entry;
|
||||
|
||||
if (!MixerContext || !EnumFunction || !EnumContext)
|
||||
{
|
||||
// invalid parameter
|
||||
return MM_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!MixerContext->Alloc || !MixerContext->Control || !MixerContext->Free || !MixerContext->Open ||
|
||||
!MixerContext->AllocEventData || !MixerContext->FreeEventData ||
|
||||
!MixerContext->Close || !MixerContext->OpenKey || !MixerContext->QueryKeyValue || !MixerContext->CloseKey)
|
||||
{
|
||||
// invalid parameter
|
||||
return MM_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// allocate a mixer list
|
||||
MixerList = (PMIXER_LIST)MixerContext->Alloc(sizeof(MIXER_LIST));
|
||||
if (!MixerList)
|
||||
{
|
||||
// no memory
|
||||
return MM_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
//initialize mixer list
|
||||
MixerList->MixerListCount = 0;
|
||||
MixerList->MixerDataCount = 0;
|
||||
MixerList->WaveInListCount = 0;
|
||||
MixerList->WaveOutListCount = 0;
|
||||
InitializeListHead(&MixerList->MixerList);
|
||||
InitializeListHead(&MixerList->MixerData);
|
||||
InitializeListHead(&MixerList->WaveInList);
|
||||
InitializeListHead(&MixerList->WaveOutList);
|
||||
|
||||
|
||||
// store mixer list
|
||||
MixerContext->MixerContext = (PVOID)MixerList;
|
||||
|
||||
// start enumerating all available devices
|
||||
Count = 0;
|
||||
DeviceIndex = 0;
|
||||
|
||||
do
|
||||
{
|
||||
// enumerate a device
|
||||
Status = EnumFunction(EnumContext, DeviceIndex, &DeviceName, &hMixer, &hKey);
|
||||
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
//check error code
|
||||
if (Status == MM_STATUS_NO_MORE_DEVICES)
|
||||
{
|
||||
// enumeration has finished
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
DPRINT1("Failed to enumerate device %lu\n", DeviceIndex);
|
||||
|
||||
// TODO cleanup
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// create a mixer data entry
|
||||
Status = MMixerCreateMixerData(MixerContext, MixerList, DeviceIndex, DeviceName, hMixer, hKey);
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
break;
|
||||
}
|
||||
|
||||
// increment device index
|
||||
DeviceIndex++;
|
||||
}while(TRUE);
|
||||
|
||||
//now all filters have been pre-opened
|
||||
// lets enumerate the filters
|
||||
Entry = MixerList->MixerData.Flink;
|
||||
while(Entry != &MixerList->MixerData)
|
||||
{
|
||||
MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry);
|
||||
MMixerSetupFilter(MixerContext, MixerList, MixerData, &Count);
|
||||
Entry = Entry->Flink;
|
||||
}
|
||||
|
||||
// done
|
||||
return MM_STATUS_SUCCESS;
|
||||
}
|
198
lib/drivers/sound/mmixer/mmixer.h
Normal file
198
lib/drivers/sound/mmixer/mmixer.h
Normal file
|
@ -0,0 +1,198 @@
|
|||
#pragma once
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MM_STATUS_SUCCESS = 0,
|
||||
MM_STATUS_NOTINITIALIZED,
|
||||
MM_STATUS_NOT_IMPLEMENTED,
|
||||
MM_STATUS_NO_MORE_DEVICES,
|
||||
MM_STATUS_MORE_ENTRIES,
|
||||
MM_STATUS_INVALID_PARAMETER,
|
||||
MM_STATUS_UNSUCCESSFUL,
|
||||
MM_STATUS_NO_MEMORY
|
||||
|
||||
|
||||
}MIXER_STATUS;
|
||||
|
||||
|
||||
typedef PVOID (*PMIXER_ALLOC)(
|
||||
IN ULONG NumberOfBytes);
|
||||
|
||||
typedef VOID (*PMIXER_FREE)(
|
||||
IN PVOID Block);
|
||||
|
||||
typedef MIXER_STATUS (*PMIXER_ENUM)(
|
||||
IN PVOID EnumContext,
|
||||
IN ULONG DeviceIndex,
|
||||
OUT LPWSTR * DeviceName,
|
||||
OUT PHANDLE OutHandle,
|
||||
OUT PHANDLE OutDevInterfaceKey);
|
||||
|
||||
typedef MIXER_STATUS(*PMIXER_DEVICE_CONTROL)(
|
||||
IN HANDLE hMixer,
|
||||
IN ULONG dwIoControlCode,
|
||||
IN PVOID lpInBuffer,
|
||||
IN ULONG nInBufferSize,
|
||||
OUT PVOID lpOutBuffer,
|
||||
ULONG nOutBufferSize,
|
||||
PULONG lpBytesReturned);
|
||||
|
||||
typedef MIXER_STATUS(*PMIXER_OPEN)(
|
||||
IN LPWSTR DevicePath,
|
||||
OUT PHANDLE hDevice);
|
||||
|
||||
typedef MIXER_STATUS(*PMIXER_CLOSE)(
|
||||
IN HANDLE hDevice);
|
||||
|
||||
typedef MIXER_STATUS(*PMIXER_CLOSEKEY)(
|
||||
IN HANDLE hKey);
|
||||
|
||||
typedef VOID (*PMIXER_EVENT)(
|
||||
IN PVOID MixerEvent);
|
||||
|
||||
typedef VOID (*PMIXER_COPY)(
|
||||
IN PVOID Dst,
|
||||
IN PVOID Src,
|
||||
IN ULONG Length);
|
||||
|
||||
typedef MIXER_STATUS(*PMIXER_QUERY_KEY_VALUE)(
|
||||
IN HANDLE hKey,
|
||||
IN LPWSTR KeyName,
|
||||
OUT PVOID * ResultBuffer,
|
||||
OUT PULONG ResultLength,
|
||||
OUT PULONG KeyType);
|
||||
|
||||
typedef MIXER_STATUS(*PMIXER_OPEN_KEY)(
|
||||
IN HANDLE hKey,
|
||||
IN LPWSTR SubKey,
|
||||
IN ULONG DesiredAccess,
|
||||
OUT PHANDLE OutKey);
|
||||
|
||||
typedef PVOID (*PMIXER_ALLOC_EVENT_DATA)(
|
||||
IN ULONG ExtraBytes);
|
||||
|
||||
typedef VOID (*PMIXER_FREE_EVENT_DATA)(
|
||||
IN PVOID EventData);
|
||||
|
||||
typedef MIXER_STATUS (*PIN_CREATE_CALLBACK)(
|
||||
IN PVOID Context,
|
||||
IN ULONG DeviceId,
|
||||
IN ULONG PinId,
|
||||
IN HANDLE hFilter,
|
||||
IN PKSPIN_CONNECT PinConnect,
|
||||
IN ACCESS_MASK DesiredAccess,
|
||||
OUT PHANDLE PinHandle);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ULONG SizeOfStruct;
|
||||
PVOID MixerContext;
|
||||
|
||||
PMIXER_ALLOC Alloc;
|
||||
PMIXER_DEVICE_CONTROL Control;
|
||||
PMIXER_FREE Free;
|
||||
PMIXER_OPEN Open;
|
||||
PMIXER_CLOSE Close;
|
||||
PMIXER_COPY Copy;
|
||||
PMIXER_OPEN_KEY OpenKey;
|
||||
PMIXER_QUERY_KEY_VALUE QueryKeyValue;
|
||||
PMIXER_CLOSEKEY CloseKey;
|
||||
PMIXER_ALLOC_EVENT_DATA AllocEventData;
|
||||
PMIXER_FREE_EVENT_DATA FreeEventData;
|
||||
}MIXER_CONTEXT, *PMIXER_CONTEXT;
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerInitialize(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN PMIXER_ENUM EnumFunction,
|
||||
IN PVOID EnumContext);
|
||||
|
||||
ULONG
|
||||
MMixerGetCount(
|
||||
IN PMIXER_CONTEXT MixerContext);
|
||||
|
||||
ULONG
|
||||
MMixerGetWaveInCount(
|
||||
IN PMIXER_CONTEXT MixerContext);
|
||||
|
||||
ULONG
|
||||
MMixerGetWaveOutCount(
|
||||
IN PMIXER_CONTEXT MixerContext);
|
||||
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetCapabilities(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN ULONG MixerIndex,
|
||||
OUT LPMIXERCAPSW MixerCaps);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerOpen(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN ULONG MixerId,
|
||||
IN PVOID MixerEvent,
|
||||
IN PMIXER_EVENT MixerEventRoutine,
|
||||
OUT PHANDLE MixerHandle);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetLineInfo(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN HANDLE MixerHandle,
|
||||
IN ULONG Flags,
|
||||
OUT LPMIXERLINEW MixerLine);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetLineControls(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN HANDLE MixerHandle,
|
||||
IN ULONG Flags,
|
||||
OUT LPMIXERLINECONTROLSW MixerLineControls);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerSetControlDetails(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN HANDLE MixerHandle,
|
||||
IN ULONG Flags,
|
||||
OUT LPMIXERCONTROLDETAILS MixerControlDetails);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetControlDetails(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN HANDLE MixerHandle,
|
||||
IN ULONG Flags,
|
||||
OUT LPMIXERCONTROLDETAILS MixerControlDetails);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerWaveOutCapabilities(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN ULONG DeviceIndex,
|
||||
OUT LPWAVEOUTCAPSW Caps);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerWaveInCapabilities(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN ULONG DeviceIndex,
|
||||
OUT LPWAVEINCAPSW Caps);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerOpenWave(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN ULONG DeviceIndex,
|
||||
IN ULONG bWaveIn,
|
||||
IN LPWAVEFORMATEX WaveFormat,
|
||||
IN PIN_CREATE_CALLBACK CreateCallback,
|
||||
IN PVOID Context,
|
||||
OUT PHANDLE PinHandle);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerSetWaveStatus(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN HANDLE PinHandle,
|
||||
IN KSSTATE State);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetWaveDevicePath(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN ULONG bWaveIn,
|
||||
IN ULONG DeviceId,
|
||||
OUT LPWSTR * DevicePath);
|
11
lib/drivers/sound/mmixer/mmixer.rbuild
Normal file
11
lib/drivers/sound/mmixer/mmixer.rbuild
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE module SYSTEM "../../../../tools/rbuild/project.dtd">
|
||||
<module name="mmixer" type="staticlibrary" allowwarnings="false" unicode="yes">
|
||||
<include base="ReactOS">include/reactos/libs/sound</include>
|
||||
<define name="NDEBUG">1</define>
|
||||
<file>controls.c</file>
|
||||
<file>filter.c</file>
|
||||
<file>mixer.c</file>
|
||||
<file>sup.c</file>
|
||||
<file>wave.c</file>
|
||||
</module>
|
281
lib/drivers/sound/mmixer/priv.h
Normal file
281
lib/drivers/sound/mmixer/priv.h
Normal file
|
@ -0,0 +1,281 @@
|
|||
#pragma once
|
||||
|
||||
#include <pseh/pseh2.h>
|
||||
#include <ntddk.h>
|
||||
|
||||
#include <windef.h>
|
||||
#define NOBITMAP
|
||||
#include <mmreg.h>
|
||||
#include <ks.h>
|
||||
#include <ksmedia.h>
|
||||
#include <mmreg.h>
|
||||
#include <mmsystem.h>
|
||||
|
||||
#include "mmixer.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#define YDEBUG
|
||||
#include <debug.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
KSEVENTDATA EventData;
|
||||
LIST_ENTRY Entry;
|
||||
}EVENT_ITEM, *LPEVENT_ITEM;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
LIST_ENTRY Entry;
|
||||
MIXERCAPSW MixCaps;
|
||||
HANDLE hMixer;
|
||||
LIST_ENTRY LineList;
|
||||
ULONG ControlId;
|
||||
LIST_ENTRY EventList;
|
||||
}MIXER_INFO, *LPMIXER_INFO;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
LIST_ENTRY Entry;
|
||||
ULONG PinId;
|
||||
HANDLE hDevice;
|
||||
MIXERLINEW Line;
|
||||
LPMIXERCONTROLW LineControls;
|
||||
PULONG NodeIds;
|
||||
LIST_ENTRY LineControlsExtraData;
|
||||
}MIXERLINE_EXT, *LPMIXERLINE_EXT;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
LIST_ENTRY Entry;
|
||||
ULONG dwControlID;
|
||||
}MIXERCONTROL_DATA, *LPMIXERCONTROL_DATA;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MIXERCONTROL_DATA Header;
|
||||
LONG SignedMinimum;
|
||||
LONG SignedMaximum;
|
||||
LONG SteppingDelta;
|
||||
ULONG InputSteppingDelta;
|
||||
ULONG ValuesCount;
|
||||
PLONG Values;
|
||||
}MIXERVOLUME_DATA, *LPMIXERVOLUME_DATA;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
LIST_ENTRY Entry;
|
||||
ULONG DeviceId;
|
||||
HANDLE hDevice;
|
||||
HANDLE hDeviceInterfaceKey;
|
||||
LPWSTR DeviceName;
|
||||
}MIXER_DATA, *LPMIXER_DATA;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
LIST_ENTRY Entry;
|
||||
ULONG DeviceId;
|
||||
ULONG PinId;
|
||||
union
|
||||
{
|
||||
WAVEOUTCAPSW OutCaps;
|
||||
WAVEINCAPSW InCaps;
|
||||
}u;
|
||||
}WAVE_INFO, *LPWAVE_INFO;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ULONG MixerListCount;
|
||||
LIST_ENTRY MixerList;
|
||||
ULONG MixerDataCount;
|
||||
LIST_ENTRY MixerData;
|
||||
ULONG WaveInListCount;
|
||||
LIST_ENTRY WaveInList;
|
||||
ULONG WaveOutListCount;
|
||||
LIST_ENTRY WaveOutList;
|
||||
}MIXER_LIST, *PMIXER_LIST;
|
||||
|
||||
#define DESTINATION_LINE 0xFFFF0000
|
||||
|
||||
ULONG
|
||||
MMixerGetFilterPinCount(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN HANDLE hMixer);
|
||||
|
||||
LPGUID
|
||||
MMixerGetNodeType(
|
||||
IN PKSMULTIPLE_ITEM MultipleItem,
|
||||
IN ULONG Index);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetNodeIndexes(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN PKSMULTIPLE_ITEM MultipleItem,
|
||||
IN ULONG NodeIndex,
|
||||
IN ULONG bNode,
|
||||
IN ULONG bFrom,
|
||||
OUT PULONG NodeReferenceCount,
|
||||
OUT PULONG *NodeReference);
|
||||
|
||||
PKSTOPOLOGY_CONNECTION
|
||||
MMixerGetConnectionByIndex(
|
||||
IN PKSMULTIPLE_ITEM MultipleItem,
|
||||
IN ULONG Index);
|
||||
|
||||
ULONG
|
||||
MMixerGetControlTypeFromTopologyNode(
|
||||
IN LPGUID NodeType);
|
||||
|
||||
LPMIXERLINE_EXT
|
||||
MMixerGetSourceMixerLineByLineId(
|
||||
LPMIXER_INFO MixerInfo,
|
||||
DWORD dwLineID);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetFilterTopologyProperty(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN HANDLE hMixer,
|
||||
IN ULONG PropertyId,
|
||||
OUT PKSMULTIPLE_ITEM * OutMultipleItem);
|
||||
|
||||
VOID
|
||||
MMixerFreeMixerInfo(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN LPMIXER_INFO MixerInfo);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetTargetPins(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN PKSMULTIPLE_ITEM NodeTypes,
|
||||
IN PKSMULTIPLE_ITEM NodeConnections,
|
||||
IN ULONG NodeIndex,
|
||||
IN ULONG bUpDirection,
|
||||
OUT PULONG Pins,
|
||||
IN ULONG PinCount);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetPhysicalConnection(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN HANDLE hMixer,
|
||||
IN ULONG PinId,
|
||||
OUT PKSPIN_PHYSICALCONNECTION *OutConnection);
|
||||
|
||||
ULONG
|
||||
MMixerGetIndexOfGuid(
|
||||
PKSMULTIPLE_ITEM MultipleItem,
|
||||
LPCGUID NodeType);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerSetupFilter(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN PMIXER_LIST MixerList,
|
||||
IN LPMIXER_DATA MixerData,
|
||||
IN PULONG DeviceCount);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetTargetPinsByNodeConnectionIndex(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN PKSMULTIPLE_ITEM NodeConnections,
|
||||
IN PKSMULTIPLE_ITEM NodeTypes,
|
||||
IN ULONG bUpDirection,
|
||||
IN ULONG NodeConnectionIndex,
|
||||
IN ULONG PinCount,
|
||||
OUT PULONG Pins);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetControlsFromPin(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN PKSMULTIPLE_ITEM NodeConnections,
|
||||
IN PKSMULTIPLE_ITEM NodeTypes,
|
||||
IN ULONG PinId,
|
||||
IN ULONG bUpDirection,
|
||||
OUT PULONG Nodes);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerVerifyContext(
|
||||
IN PMIXER_CONTEXT MixerContext);
|
||||
|
||||
LPMIXER_INFO
|
||||
MMixerGetMixerInfoByIndex(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN ULONG MixerIndex);
|
||||
|
||||
LPMIXERLINE_EXT
|
||||
MMixerGetSourceMixerLineByComponentType(
|
||||
LPMIXER_INFO MixerInfo,
|
||||
DWORD dwComponentType);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetMixerControlById(
|
||||
LPMIXER_INFO MixerInfo,
|
||||
DWORD dwControlID,
|
||||
LPMIXERLINE_EXT *MixerLine,
|
||||
LPMIXERCONTROLW *MixerControl,
|
||||
PULONG NodeId);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerSetGetMuteControlDetails(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN HANDLE hMixer,
|
||||
IN ULONG NodeId,
|
||||
IN ULONG dwLineID,
|
||||
IN LPMIXERCONTROLDETAILS MixerControlDetails,
|
||||
IN ULONG bSet);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerSetGetVolumeControlDetails(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN HANDLE hMixer,
|
||||
IN ULONG NodeId,
|
||||
IN ULONG bSet,
|
||||
LPMIXERCONTROLW MixerControl,
|
||||
IN LPMIXERCONTROLDETAILS MixerControlDetails,
|
||||
LPMIXERLINE_EXT MixerLine);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerSetGetControlDetails(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN HANDLE hMixer,
|
||||
IN ULONG NodeId,
|
||||
IN ULONG bSet,
|
||||
IN ULONG PropertyId,
|
||||
IN ULONG Channel,
|
||||
IN PLONG InputValue);
|
||||
|
||||
LPMIXER_DATA
|
||||
MMixerGetDataByDeviceId(
|
||||
IN PMIXER_LIST MixerList,
|
||||
IN ULONG DeviceId);
|
||||
|
||||
LPMIXER_DATA
|
||||
MMixerGetDataByDeviceName(
|
||||
IN PMIXER_LIST MixerList,
|
||||
IN LPWSTR DeviceName);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerCreateMixerData(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN PMIXER_LIST MixerList,
|
||||
IN ULONG DeviceId,
|
||||
IN LPWSTR DeviceName,
|
||||
IN HANDLE hDevice,
|
||||
IN HANDLE hKey);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetDeviceName(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN LPMIXER_INFO MixerInfo,
|
||||
IN HANDLE hKey);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerInitializeWaveInfo(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN PMIXER_LIST MixerList,
|
||||
IN LPMIXER_DATA MixerData,
|
||||
IN LPWSTR DeviceName,
|
||||
IN ULONG bWaveIn,
|
||||
IN ULONG PinId);
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerAddEvents(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN OUT LPMIXER_INFO MixerInfo);
|
673
lib/drivers/sound/mmixer/sup.c
Normal file
673
lib/drivers/sound/mmixer/sup.c
Normal file
|
@ -0,0 +1,673 @@
|
|||
/*
|
||||
* COPYRIGHT: See COPYING in the top level directory
|
||||
* PROJECT: ReactOS Kernel Streaming
|
||||
* FILE: lib/drivers/sound/mmixer/sup.c
|
||||
* PURPOSE: Mixer Support Functions
|
||||
* PROGRAMMER: Johannes Anderwald
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "priv.h"
|
||||
|
||||
const GUID KSNODETYPE_SUM = {0xDA441A60L, 0xC556, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
|
||||
const GUID KSNODETYPE_DAC = {0x507AE360L, 0xC554, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
|
||||
const GUID KSNODETYPE_ADC = {0x4D837FE0L, 0xC555, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
|
||||
const GUID KSNODETYPE_AGC = {0xE88C9BA0L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
|
||||
const GUID KSNODETYPE_LOUDNESS = {0x41887440L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
|
||||
const GUID KSNODETYPE_MUTE = {0x02B223C0L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
|
||||
const GUID KSNODETYPE_TONE = {0x7607E580L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
|
||||
const GUID KSNODETYPE_VOLUME = {0x3A5ACC00L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
|
||||
const GUID KSNODETYPE_PEAKMETER = {0xa085651e, 0x5f0d, 0x4b36, {0xa8, 0x69, 0xd1, 0x95, 0xd6, 0xab, 0x4b, 0x9e}};
|
||||
const GUID KSNODETYPE_MUX = {0x2CEAF780, 0xC556, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
|
||||
const GUID KSNODETYPE_STEREO_WIDE = {0xA9E69800L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
|
||||
const GUID KSNODETYPE_CHORUS = {0x20173F20L, 0xC559, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
|
||||
const GUID KSNODETYPE_REVERB = {0xEF0328E0L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
|
||||
const GUID KSNODETYPE_SUPERMIX = {0xE573ADC0L, 0xC555, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
|
||||
|
||||
const GUID KSPROPSETID_Audio = {0x45FFAAA0L, 0x6E1B, 0x11D0, {0xBC, 0xF2, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}};
|
||||
const GUID KSPROPSETID_Pin = {0x8C134960L, 0x51AD, 0x11CF, {0x87, 0x8A, 0x94, 0xF8, 0x01, 0xC1, 0x00, 0x00}};
|
||||
const GUID KSPROPSETID_General = {0x1464EDA5L, 0x6A8F, 0x11D1, {0x9A, 0xA7, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
|
||||
const GUID KSPROPSETID_Topology = {0x720D4AC0L, 0x7533, 0x11D0, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
|
||||
const GUID KSEVENTSETID_AudioControlChange = {0xE85E9698L, 0xFA2F, 0x11D1, {0x95, 0xBD, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3}};
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerVerifyContext(
|
||||
IN PMIXER_CONTEXT MixerContext)
|
||||
{
|
||||
if (MixerContext->SizeOfStruct != sizeof(MIXER_CONTEXT))
|
||||
return MM_STATUS_INVALID_PARAMETER;
|
||||
|
||||
if (!MixerContext->Alloc || !MixerContext->Control || !MixerContext->Free || !MixerContext->Open ||
|
||||
!MixerContext->AllocEventData || !MixerContext->FreeEventData ||
|
||||
!MixerContext->Close || !MixerContext->OpenKey || !MixerContext->QueryKeyValue || !MixerContext->CloseKey)
|
||||
return MM_STATUS_INVALID_PARAMETER;
|
||||
|
||||
if (!MixerContext->MixerContext)
|
||||
return MM_STATUS_INVALID_PARAMETER;
|
||||
|
||||
return MM_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
VOID
|
||||
MMixerFreeMixerInfo(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN LPMIXER_INFO MixerInfo)
|
||||
{
|
||||
//UNIMPLEMENTED
|
||||
// FIXME
|
||||
// free all lines
|
||||
|
||||
MixerContext->Free((PVOID)MixerInfo);
|
||||
}
|
||||
|
||||
LPMIXER_INFO
|
||||
MMixerGetMixerInfoByIndex(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN ULONG MixerIndex)
|
||||
{
|
||||
LPMIXER_INFO MixerInfo;
|
||||
PLIST_ENTRY Entry;
|
||||
PMIXER_LIST MixerList;
|
||||
ULONG Index = 0;
|
||||
|
||||
// get mixer list
|
||||
MixerList = (PMIXER_LIST)MixerContext->MixerContext;
|
||||
|
||||
if (!MixerList->MixerListCount)
|
||||
return NULL;
|
||||
|
||||
Entry = MixerList->MixerList.Flink;
|
||||
|
||||
while(Entry != &MixerList->MixerList)
|
||||
{
|
||||
MixerInfo = (LPMIXER_INFO)CONTAINING_RECORD(Entry, MIXER_INFO, Entry);
|
||||
|
||||
if (Index == MixerIndex)
|
||||
return MixerInfo;
|
||||
|
||||
// move to next mixer entry
|
||||
Index++;
|
||||
Entry = Entry->Flink;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LPMIXERCONTROL_DATA
|
||||
MMixerGetMixerControlDataById(
|
||||
PLIST_ENTRY ListHead,
|
||||
DWORD dwControlId)
|
||||
{
|
||||
PLIST_ENTRY Entry;
|
||||
LPMIXERCONTROL_DATA Control;
|
||||
|
||||
/* get first entry */
|
||||
Entry = ListHead->Flink;
|
||||
|
||||
while(Entry != ListHead)
|
||||
{
|
||||
Control = (LPMIXERCONTROL_DATA)CONTAINING_RECORD(Entry, MIXERCONTROL_DATA, Entry);
|
||||
DPRINT("dwSource %x dwSource %x\n", Control->dwControlID, dwControlId);
|
||||
if (Control->dwControlID == dwControlId)
|
||||
return Control;
|
||||
|
||||
Entry = Entry->Flink;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LPMIXERLINE_EXT
|
||||
MMixerGetSourceMixerLineByLineId(
|
||||
LPMIXER_INFO MixerInfo,
|
||||
DWORD dwLineID)
|
||||
{
|
||||
PLIST_ENTRY Entry;
|
||||
LPMIXERLINE_EXT MixerLineSrc;
|
||||
|
||||
/* get first entry */
|
||||
Entry = MixerInfo->LineList.Flink;
|
||||
|
||||
while(Entry != &MixerInfo->LineList)
|
||||
{
|
||||
MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
|
||||
DPRINT("dwLineID %x dwLineID %x MixerLineSrc %p\n", MixerLineSrc->Line.dwLineID, dwLineID, MixerLineSrc);
|
||||
if (MixerLineSrc->Line.dwLineID == dwLineID)
|
||||
return MixerLineSrc;
|
||||
|
||||
Entry = Entry->Flink;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ULONG
|
||||
MMixerGetIndexOfGuid(
|
||||
PKSMULTIPLE_ITEM MultipleItem,
|
||||
LPCGUID NodeType)
|
||||
{
|
||||
ULONG Index;
|
||||
LPGUID Guid;
|
||||
|
||||
Guid = (LPGUID)(MultipleItem+1);
|
||||
|
||||
/* iterate through node type array */
|
||||
for(Index = 0; Index < MultipleItem->Count; Index++)
|
||||
{
|
||||
if (IsEqualGUIDAligned(NodeType, Guid))
|
||||
{
|
||||
/* found matching guid */
|
||||
return Index;
|
||||
}
|
||||
Guid++;
|
||||
}
|
||||
return MAXULONG;
|
||||
}
|
||||
|
||||
PKSTOPOLOGY_CONNECTION
|
||||
MMixerGetConnectionByIndex(
|
||||
IN PKSMULTIPLE_ITEM MultipleItem,
|
||||
IN ULONG Index)
|
||||
{
|
||||
PKSTOPOLOGY_CONNECTION Descriptor;
|
||||
|
||||
ASSERT(Index < MultipleItem->Count);
|
||||
|
||||
Descriptor = (PKSTOPOLOGY_CONNECTION)(MultipleItem + 1);
|
||||
return &Descriptor[Index];
|
||||
}
|
||||
|
||||
LPGUID
|
||||
MMixerGetNodeType(
|
||||
IN PKSMULTIPLE_ITEM MultipleItem,
|
||||
IN ULONG Index)
|
||||
{
|
||||
LPGUID NodeType;
|
||||
|
||||
ASSERT(Index < MultipleItem->Count);
|
||||
|
||||
NodeType = (LPGUID)(MultipleItem + 1);
|
||||
return &NodeType[Index];
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetNodeIndexes(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN PKSMULTIPLE_ITEM MultipleItem,
|
||||
IN ULONG NodeIndex,
|
||||
IN ULONG bNode,
|
||||
IN ULONG bFrom,
|
||||
OUT PULONG NodeReferenceCount,
|
||||
OUT PULONG *NodeReference)
|
||||
{
|
||||
ULONG Index, Count = 0;
|
||||
PKSTOPOLOGY_CONNECTION Connection;
|
||||
PULONG Refs;
|
||||
|
||||
// KSMULTIPLE_ITEM is followed by several KSTOPOLOGY_CONNECTION
|
||||
Connection = (PKSTOPOLOGY_CONNECTION)(MultipleItem + 1);
|
||||
|
||||
// first count all referenced nodes
|
||||
for(Index = 0; Index < MultipleItem->Count; Index++)
|
||||
{
|
||||
if (bNode)
|
||||
{
|
||||
if (bFrom)
|
||||
{
|
||||
if (Connection->FromNode == NodeIndex)
|
||||
{
|
||||
// node id has a connection
|
||||
Count++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Connection->ToNode == NodeIndex)
|
||||
{
|
||||
// node id has a connection
|
||||
Count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bFrom)
|
||||
{
|
||||
if (Connection->FromNodePin == NodeIndex && Connection->FromNode == KSFILTER_NODE)
|
||||
{
|
||||
// node id has a connection
|
||||
Count++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Connection->ToNodePin == NodeIndex && Connection->ToNode == KSFILTER_NODE)
|
||||
{
|
||||
// node id has a connection
|
||||
Count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// move to next connection
|
||||
Connection++;
|
||||
}
|
||||
|
||||
if (!Count)
|
||||
{
|
||||
*NodeReferenceCount = 0;
|
||||
*NodeReference = NULL;
|
||||
return MM_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
ASSERT(Count != 0);
|
||||
|
||||
/* now allocate node index array */
|
||||
Refs = (PULONG)MixerContext->Alloc(sizeof(ULONG) * Count);
|
||||
if (!Refs)
|
||||
{
|
||||
// not enough memory
|
||||
return MM_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
Count = 0;
|
||||
Connection = (PKSTOPOLOGY_CONNECTION)(MultipleItem + 1);
|
||||
for(Index = 0; Index < MultipleItem->Count; Index++)
|
||||
{
|
||||
if (bNode)
|
||||
{
|
||||
if (bFrom)
|
||||
{
|
||||
if (Connection->FromNode == NodeIndex)
|
||||
{
|
||||
/* node id has a connection */
|
||||
Refs[Count] = Index;
|
||||
Count++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Connection->ToNode == NodeIndex)
|
||||
{
|
||||
/* node id has a connection */
|
||||
Refs[Count] = Index;
|
||||
Count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bFrom)
|
||||
{
|
||||
if (Connection->FromNodePin == NodeIndex && Connection->FromNode == KSFILTER_NODE)
|
||||
{
|
||||
/* node id has a connection */
|
||||
Refs[Count] = Index;
|
||||
Count++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Connection->ToNodePin == NodeIndex && Connection->ToNode == KSFILTER_NODE)
|
||||
{
|
||||
/* node id has a connection */
|
||||
Refs[Count] = Index;
|
||||
Count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* move to next connection */
|
||||
Connection++;
|
||||
}
|
||||
|
||||
/* store result */
|
||||
*NodeReference = Refs;
|
||||
*NodeReferenceCount = Count;
|
||||
|
||||
return MM_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetTargetPins(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN PKSMULTIPLE_ITEM NodeTypes,
|
||||
IN PKSMULTIPLE_ITEM NodeConnections,
|
||||
IN ULONG NodeIndex,
|
||||
IN ULONG bUpDirection,
|
||||
OUT PULONG Pins,
|
||||
IN ULONG PinCount)
|
||||
{
|
||||
ULONG NodeConnectionCount, Index;
|
||||
MIXER_STATUS Status;
|
||||
PULONG NodeConnection;
|
||||
|
||||
// sanity check */
|
||||
ASSERT(NodeIndex != (ULONG)-1);
|
||||
|
||||
/* get all node indexes referenced by that pin */
|
||||
if (bUpDirection)
|
||||
Status = MMixerGetNodeIndexes(MixerContext, NodeConnections, NodeIndex, TRUE, FALSE, &NodeConnectionCount, &NodeConnection);
|
||||
else
|
||||
Status = MMixerGetNodeIndexes(MixerContext, NodeConnections, NodeIndex, TRUE, TRUE, &NodeConnectionCount, &NodeConnection);
|
||||
|
||||
//DPRINT("NodeIndex %u Status %x Count %u\n", NodeIndex, Status, NodeConnectionCount);
|
||||
|
||||
if (Status == MM_STATUS_SUCCESS)
|
||||
{
|
||||
for(Index = 0; Index < NodeConnectionCount; Index++)
|
||||
{
|
||||
Status = MMixerGetTargetPinsByNodeConnectionIndex(MixerContext, NodeConnections, NodeTypes, bUpDirection, NodeConnection[Index], PinCount, Pins);
|
||||
ASSERT(Status == STATUS_SUCCESS);
|
||||
}
|
||||
MixerContext->Free((PVOID)NodeConnection);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
LPMIXERLINE_EXT
|
||||
MMixerGetSourceMixerLineByComponentType(
|
||||
LPMIXER_INFO MixerInfo,
|
||||
DWORD dwComponentType)
|
||||
{
|
||||
PLIST_ENTRY Entry;
|
||||
LPMIXERLINE_EXT MixerLineSrc;
|
||||
|
||||
/* get first entry */
|
||||
Entry = MixerInfo->LineList.Flink;
|
||||
|
||||
while(Entry != &MixerInfo->LineList)
|
||||
{
|
||||
MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
|
||||
if (MixerLineSrc->Line.dwComponentType == dwComponentType)
|
||||
return MixerLineSrc;
|
||||
|
||||
Entry = Entry->Flink;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetMixerControlById(
|
||||
LPMIXER_INFO MixerInfo,
|
||||
DWORD dwControlID,
|
||||
LPMIXERLINE_EXT *MixerLine,
|
||||
LPMIXERCONTROLW *MixerControl,
|
||||
PULONG NodeId)
|
||||
{
|
||||
PLIST_ENTRY Entry;
|
||||
LPMIXERLINE_EXT MixerLineSrc;
|
||||
ULONG Index;
|
||||
|
||||
/* get first entry */
|
||||
Entry = MixerInfo->LineList.Flink;
|
||||
|
||||
while(Entry != &MixerInfo->LineList)
|
||||
{
|
||||
MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
|
||||
|
||||
for(Index = 0; Index < MixerLineSrc->Line.cControls; Index++)
|
||||
{
|
||||
if (MixerLineSrc->LineControls[Index].dwControlID == dwControlID)
|
||||
{
|
||||
if (MixerLine)
|
||||
*MixerLine = MixerLineSrc;
|
||||
if (MixerControl)
|
||||
*MixerControl = &MixerLineSrc->LineControls[Index];
|
||||
if (NodeId)
|
||||
*NodeId = MixerLineSrc->NodeIds[Index];
|
||||
return MM_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
Entry = Entry->Flink;
|
||||
}
|
||||
|
||||
return MM_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
ULONG
|
||||
MMixerGetVolumeControlIndex(
|
||||
LPMIXERVOLUME_DATA VolumeData,
|
||||
LONG Value)
|
||||
{
|
||||
ULONG Index;
|
||||
|
||||
for(Index = 0; Index < VolumeData->ValuesCount; Index++)
|
||||
{
|
||||
if (VolumeData->Values[Index] > Value)
|
||||
{
|
||||
return VolumeData->InputSteppingDelta * Index;
|
||||
}
|
||||
}
|
||||
return VolumeData->InputSteppingDelta * (VolumeData->ValuesCount-1);
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerSetGetMuteControlDetails(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN HANDLE hMixer,
|
||||
IN ULONG NodeId,
|
||||
IN ULONG dwLineID,
|
||||
IN LPMIXERCONTROLDETAILS MixerControlDetails,
|
||||
IN ULONG bSet)
|
||||
{
|
||||
LPMIXERCONTROLDETAILS_BOOLEAN Input;
|
||||
LONG Value;
|
||||
MIXER_STATUS Status;
|
||||
|
||||
if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN))
|
||||
return MM_STATUS_INVALID_PARAMETER;
|
||||
|
||||
/* get input */
|
||||
Input = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails;
|
||||
|
||||
/* FIXME SEH */
|
||||
if (bSet)
|
||||
Value = Input->fValue;
|
||||
|
||||
/* set control details */
|
||||
Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_MUTE, 0, &Value);
|
||||
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
return Status;
|
||||
|
||||
/* FIXME SEH */
|
||||
if (!bSet)
|
||||
{
|
||||
Input->fValue = Value;
|
||||
return Status;
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIXME notify wdmaud clients MM_MIXM_LINE_CHANGE dwLineID
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerSetGetVolumeControlDetails(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN HANDLE hMixer,
|
||||
IN ULONG NodeId,
|
||||
IN ULONG bSet,
|
||||
LPMIXERCONTROLW MixerControl,
|
||||
IN LPMIXERCONTROLDETAILS MixerControlDetails,
|
||||
LPMIXERLINE_EXT MixerLine)
|
||||
{
|
||||
LPMIXERCONTROLDETAILS_UNSIGNED Input;
|
||||
LONG Value, Index, Channel = 0;
|
||||
ULONG dwValue;
|
||||
MIXER_STATUS Status;
|
||||
LPMIXERVOLUME_DATA VolumeData;
|
||||
|
||||
if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_SIGNED))
|
||||
return MM_STATUS_INVALID_PARAMETER;
|
||||
|
||||
VolumeData = (LPMIXERVOLUME_DATA)MMixerGetMixerControlDataById(&MixerLine->LineControlsExtraData, MixerControl->dwControlID);
|
||||
if (!VolumeData)
|
||||
return MM_STATUS_UNSUCCESSFUL;
|
||||
|
||||
/* get input */
|
||||
Input = (LPMIXERCONTROLDETAILS_UNSIGNED)MixerControlDetails->paDetails;
|
||||
|
||||
if (bSet)
|
||||
{
|
||||
/* FIXME SEH */
|
||||
Value = Input->dwValue;
|
||||
Index = Value / VolumeData->InputSteppingDelta;
|
||||
|
||||
if (Index >= VolumeData->ValuesCount)
|
||||
{
|
||||
DPRINT1("Index %u out of bounds %u \n", Index, VolumeData->ValuesCount);
|
||||
DbgBreakPoint();
|
||||
return MM_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Value = VolumeData->Values[Index];
|
||||
}
|
||||
|
||||
/* set control details */
|
||||
if (bSet)
|
||||
{
|
||||
/* TODO */
|
||||
Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 0, &Value);
|
||||
Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 1, &Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, Channel, &Value);
|
||||
}
|
||||
|
||||
if (!bSet)
|
||||
{
|
||||
dwValue = MMixerGetVolumeControlIndex(VolumeData, (LONG)Value);
|
||||
/* FIXME SEH */
|
||||
Input->dwValue = dwValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* notify clients of a line change MM_MIXM_CONTROL_CHANGE with MixerControl->dwControlID */
|
||||
}
|
||||
return Status;
|
||||
}
|
||||
|
||||
LPMIXER_DATA
|
||||
MMixerGetDataByDeviceId(
|
||||
IN PMIXER_LIST MixerList,
|
||||
IN ULONG DeviceId)
|
||||
{
|
||||
PLIST_ENTRY Entry;
|
||||
LPMIXER_DATA MixerData;
|
||||
|
||||
Entry = MixerList->MixerData.Flink;
|
||||
while(Entry != &MixerList->MixerData)
|
||||
{
|
||||
MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry);
|
||||
if (MixerData->DeviceId == DeviceId)
|
||||
{
|
||||
return MixerData;
|
||||
}
|
||||
Entry = Entry->Flink;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LPMIXER_DATA
|
||||
MMixerGetDataByDeviceName(
|
||||
IN PMIXER_LIST MixerList,
|
||||
IN LPWSTR DeviceName)
|
||||
{
|
||||
PLIST_ENTRY Entry;
|
||||
LPMIXER_DATA MixerData;
|
||||
|
||||
Entry = MixerList->MixerData.Flink;
|
||||
while(Entry != &MixerList->MixerData)
|
||||
{
|
||||
MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry);
|
||||
if (wcsicmp(&DeviceName[2], &MixerData->DeviceName[2]) == 0)
|
||||
{
|
||||
// found entry
|
||||
return MixerData;
|
||||
}
|
||||
Entry = Entry->Flink;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerCreateMixerData(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN PMIXER_LIST MixerList,
|
||||
IN ULONG DeviceId,
|
||||
IN LPWSTR DeviceName,
|
||||
IN HANDLE hDevice,
|
||||
IN HANDLE hKey)
|
||||
{
|
||||
LPMIXER_DATA MixerData;
|
||||
|
||||
MixerData = (LPMIXER_DATA)MixerContext->Alloc(sizeof(MIXER_DATA));
|
||||
if (!MixerData)
|
||||
return MM_STATUS_NO_MEMORY;
|
||||
|
||||
MixerData->DeviceId = DeviceId;
|
||||
MixerData->DeviceName = DeviceName;
|
||||
MixerData->hDevice = hDevice;
|
||||
MixerData->hDeviceInterfaceKey = hKey;
|
||||
|
||||
InsertTailList(&MixerList->MixerData, &MixerData->Entry);
|
||||
MixerList->MixerDataCount++;
|
||||
return MM_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetDeviceName(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN LPMIXER_INFO MixerInfo,
|
||||
IN HANDLE hKey)
|
||||
{
|
||||
LPWSTR Name;
|
||||
HANDLE hTemp;
|
||||
ULONG Length;
|
||||
ULONG Type;
|
||||
MIXER_STATUS Status;
|
||||
|
||||
Status = MixerContext->QueryKeyValue(hKey, L"FriendlyName", (PVOID*)&Name, &Length, &Type);
|
||||
if (Status == MM_STATUS_SUCCESS)
|
||||
{
|
||||
// copy device name
|
||||
MixerContext->Copy(MixerInfo->MixCaps.szPname, Name, min(wcslen(Name), MAXPNAMELEN-1) * sizeof(WCHAR));
|
||||
|
||||
// make sure its null terminated
|
||||
MixerInfo->MixCaps.szPname[MAXPNAMELEN-1] = L'\0';
|
||||
|
||||
// free device name
|
||||
MixerContext->Free(Name);
|
||||
|
||||
// done
|
||||
return Status;
|
||||
}
|
||||
|
||||
Status = MixerContext->OpenKey(hKey, L"Device Parameters", KEY_READ, &hTemp);
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
return Status;
|
||||
|
||||
Status = MixerContext->QueryKeyValue(hKey, L"FriendlyName", (PVOID*)&Name, &Length, &Type);
|
||||
if (Status == MM_STATUS_SUCCESS)
|
||||
{
|
||||
// copy device name
|
||||
MixerContext->Copy(MixerInfo->MixCaps.szPname, Name, min(wcslen(Name), MAXPNAMELEN-1) * sizeof(WCHAR));
|
||||
|
||||
// make sure its null terminated
|
||||
MixerInfo->MixCaps.szPname[MAXPNAMELEN-1] = L'\0';
|
||||
|
||||
// free device name
|
||||
MixerContext->Free(Name);
|
||||
}
|
||||
|
||||
MixerContext->CloseKey(hTemp);
|
||||
return Status;
|
||||
}
|
683
lib/drivers/sound/mmixer/wave.c
Normal file
683
lib/drivers/sound/mmixer/wave.c
Normal file
|
@ -0,0 +1,683 @@
|
|||
/*
|
||||
* COPYRIGHT: See COPYING in the top level directory
|
||||
* PROJECT: ReactOS Kernel Streaming
|
||||
* FILE: lib/drivers/sound/mmixer/wave.c
|
||||
* PURPOSE: Wave Handling Functions
|
||||
* PROGRAMMER: Johannes Anderwald
|
||||
*/
|
||||
|
||||
#include "priv.h"
|
||||
|
||||
const GUID KSPROPSETID_Connection = {0x1D58C920L, 0xAC9B, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
|
||||
const GUID KSDATAFORMAT_SPECIFIER_WAVEFORMATEX = {0x05589f81L, 0xc356, 0x11ce, {0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a}};
|
||||
const GUID KSDATAFORMAT_SUBTYPE_PCM = {0x00000001L, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
|
||||
const GUID KSDATAFORMAT_TYPE_AUDIO = {0x73647561L, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
|
||||
const GUID KSINTERFACESETID_Standard = {0x1A8766A0L, 0x62CE, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
|
||||
const GUID KSMEDIUMSETID_Standard = {0x4747B320L, 0x62CE, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ULONG SampleRate;
|
||||
ULONG Bit8Mono;
|
||||
ULONG Bit8Stereo;
|
||||
ULONG Bit16Mono;
|
||||
ULONG Bit16Stereo;
|
||||
}AUDIO_RANGE;
|
||||
|
||||
#define AUDIO_TEST_RANGE (5)
|
||||
|
||||
static AUDIO_RANGE TestRange[AUDIO_TEST_RANGE] =
|
||||
{
|
||||
{
|
||||
11025,
|
||||
WAVE_FORMAT_1M08,
|
||||
WAVE_FORMAT_1S08,
|
||||
WAVE_FORMAT_1M16,
|
||||
WAVE_FORMAT_1S16
|
||||
},
|
||||
{
|
||||
22050,
|
||||
WAVE_FORMAT_2M08,
|
||||
WAVE_FORMAT_2S08,
|
||||
WAVE_FORMAT_2M16,
|
||||
WAVE_FORMAT_2S16
|
||||
},
|
||||
{
|
||||
44100,
|
||||
WAVE_FORMAT_4M08,
|
||||
WAVE_FORMAT_4S08,
|
||||
WAVE_FORMAT_4M16,
|
||||
WAVE_FORMAT_4S16
|
||||
},
|
||||
{
|
||||
48000,
|
||||
WAVE_FORMAT_48M08,
|
||||
WAVE_FORMAT_48S08,
|
||||
WAVE_FORMAT_48M16,
|
||||
WAVE_FORMAT_48S16
|
||||
},
|
||||
{
|
||||
96000,
|
||||
WAVE_FORMAT_96M08,
|
||||
WAVE_FORMAT_96S08,
|
||||
WAVE_FORMAT_96M16,
|
||||
WAVE_FORMAT_96S16
|
||||
}
|
||||
};
|
||||
|
||||
PKSPIN_CONNECT
|
||||
MMixerAllocatePinConnect(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
ULONG DataFormatSize)
|
||||
{
|
||||
return MixerContext->Alloc(sizeof(KSPIN_CONNECT) + DataFormatSize);
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetWaveInfoByIndexAndType(
|
||||
IN PMIXER_LIST MixerList,
|
||||
IN ULONG DeviceIndex,
|
||||
IN ULONG bWaveInType,
|
||||
OUT LPWAVE_INFO *OutWaveInfo)
|
||||
{
|
||||
ULONG Index = 0;
|
||||
PLIST_ENTRY Entry, ListHead;
|
||||
LPWAVE_INFO WaveInfo;
|
||||
|
||||
if (bWaveInType)
|
||||
ListHead = &MixerList->WaveInList;
|
||||
else
|
||||
ListHead = &MixerList->WaveOutList;
|
||||
|
||||
/* get first entry */
|
||||
Entry = ListHead->Flink;
|
||||
|
||||
while(Entry != ListHead)
|
||||
{
|
||||
WaveInfo = (LPWAVE_INFO)CONTAINING_RECORD(Entry, WAVE_INFO, Entry);
|
||||
|
||||
if (Index == DeviceIndex)
|
||||
{
|
||||
*OutWaveInfo = WaveInfo;
|
||||
return MM_STATUS_SUCCESS;
|
||||
}
|
||||
Index++;
|
||||
Entry = Entry->Flink;
|
||||
}
|
||||
|
||||
return MM_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
|
||||
VOID
|
||||
MMixerInitializePinConnect(
|
||||
IN OUT PKSPIN_CONNECT PinConnect,
|
||||
IN ULONG PinId)
|
||||
{
|
||||
PinConnect->Interface.Set = KSINTERFACESETID_Standard;
|
||||
PinConnect->Interface.Id = KSINTERFACE_STANDARD_STREAMING;
|
||||
PinConnect->Interface.Flags = 0;
|
||||
PinConnect->Medium.Set = KSMEDIUMSETID_Standard;
|
||||
PinConnect->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE;
|
||||
PinConnect->Medium.Flags = 0;
|
||||
PinConnect->PinToHandle = NULL;
|
||||
PinConnect->PinId = PinId;
|
||||
PinConnect->Priority.PriorityClass = KSPRIORITY_NORMAL;
|
||||
PinConnect->Priority.PrioritySubClass = 1;
|
||||
}
|
||||
|
||||
VOID
|
||||
MMixerInitializeDataFormat(
|
||||
IN PKSDATAFORMAT_WAVEFORMATEX DataFormat,
|
||||
LPWAVEFORMATEX WaveFormatEx)
|
||||
{
|
||||
|
||||
DataFormat->WaveFormatEx.wFormatTag = WaveFormatEx->wFormatTag;
|
||||
DataFormat->WaveFormatEx.nChannels = WaveFormatEx->nChannels;
|
||||
DataFormat->WaveFormatEx.nSamplesPerSec = WaveFormatEx->nSamplesPerSec;
|
||||
DataFormat->WaveFormatEx.nBlockAlign = WaveFormatEx->nBlockAlign;
|
||||
DataFormat->WaveFormatEx.nAvgBytesPerSec = WaveFormatEx->nAvgBytesPerSec;
|
||||
DataFormat->WaveFormatEx.wBitsPerSample = WaveFormatEx->wBitsPerSample;
|
||||
DataFormat->WaveFormatEx.cbSize = 0;
|
||||
DataFormat->DataFormat.FormatSize = sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATEX);
|
||||
DataFormat->DataFormat.Flags = 0;
|
||||
DataFormat->DataFormat.Reserved = 0;
|
||||
DataFormat->DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO;
|
||||
|
||||
DataFormat->DataFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
DataFormat->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
|
||||
DataFormat->DataFormat.SampleSize = 4;
|
||||
}
|
||||
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetAudioPinDataRanges(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN HANDLE hDevice,
|
||||
IN ULONG PinId,
|
||||
IN OUT PKSMULTIPLE_ITEM * OutMultipleItem)
|
||||
{
|
||||
KSP_PIN PinProperty;
|
||||
ULONG BytesReturned = 0;
|
||||
MIXER_STATUS Status;
|
||||
PKSMULTIPLE_ITEM MultipleItem;
|
||||
|
||||
/* retrieve size of data ranges buffer */
|
||||
PinProperty.Reserved = 0;
|
||||
PinProperty.PinId = PinId;
|
||||
PinProperty.Property.Set = KSPROPSETID_Pin;
|
||||
PinProperty.Property.Id = KSPROPERTY_PIN_DATARANGES;
|
||||
PinProperty.Property.Flags = KSPROPERTY_TYPE_GET;
|
||||
|
||||
Status = MixerContext->Control(hDevice, IOCTL_KS_PROPERTY, (PVOID)&PinProperty, sizeof(KSP_PIN), (PVOID)NULL, 0, &BytesReturned);
|
||||
if (Status != MM_STATUS_MORE_ENTRIES)
|
||||
{
|
||||
return Status;
|
||||
}
|
||||
|
||||
MultipleItem = MixerContext->Alloc(BytesReturned);
|
||||
if (!MultipleItem)
|
||||
{
|
||||
/* not enough memory */
|
||||
return MM_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
Status = MixerContext->Control(hDevice, IOCTL_KS_PROPERTY, (PVOID)&PinProperty, sizeof(KSP_PIN), (PVOID)MultipleItem, BytesReturned, &BytesReturned);
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
/* failed */
|
||||
MixerContext->Free(MultipleItem);
|
||||
return Status;
|
||||
}
|
||||
|
||||
/* save result */
|
||||
*OutMultipleItem = MultipleItem;
|
||||
return Status;
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerFindAudioDataRange(
|
||||
PKSMULTIPLE_ITEM MultipleItem,
|
||||
PKSDATARANGE_AUDIO * OutDataRangeAudio)
|
||||
{
|
||||
ULONG Index;
|
||||
PKSDATARANGE_AUDIO DataRangeAudio;
|
||||
PKSDATARANGE DataRange;
|
||||
|
||||
DataRange = (PKSDATARANGE) (MultipleItem + 1);
|
||||
for(Index = 0; Index < MultipleItem->Count; Index++)
|
||||
{
|
||||
if (DataRange->FormatSize == sizeof(KSDATARANGE_AUDIO))
|
||||
{
|
||||
DataRangeAudio = (PKSDATARANGE_AUDIO)DataRange;
|
||||
if (IsEqualGUIDAligned(&DataRangeAudio->DataRange.MajorFormat, &KSDATAFORMAT_TYPE_AUDIO) &&
|
||||
IsEqualGUIDAligned(&DataRangeAudio->DataRange.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM) &&
|
||||
IsEqualGUIDAligned(&DataRangeAudio->DataRange.Specifier, &KSDATAFORMAT_SPECIFIER_WAVEFORMATEX))
|
||||
{
|
||||
DPRINT("Min Sample %u Max Sample %u Min Bits %u Max Bits %u Max Channel %u\n", DataRangeAudio->MinimumSampleFrequency, DataRangeAudio->MaximumSampleFrequency,
|
||||
DataRangeAudio->MinimumBitsPerSample, DataRangeAudio->MaximumBitsPerSample, DataRangeAudio->MaximumChannels);
|
||||
*OutDataRangeAudio = DataRangeAudio;
|
||||
return MM_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
DataRange = (PKSDATARANGE)((ULONG_PTR)DataRange + DataRange->FormatSize);
|
||||
}
|
||||
return MM_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerOpenWavePin(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN PMIXER_LIST MixerList,
|
||||
IN ULONG DeviceId,
|
||||
IN ULONG PinId,
|
||||
IN LPWAVEFORMATEX WaveFormatEx,
|
||||
IN ACCESS_MASK DesiredAccess,
|
||||
IN PIN_CREATE_CALLBACK CreateCallback,
|
||||
IN PVOID Context,
|
||||
OUT PHANDLE PinHandle)
|
||||
{
|
||||
PKSPIN_CONNECT PinConnect;
|
||||
PKSDATAFORMAT_WAVEFORMATEX DataFormat;
|
||||
LPMIXER_DATA MixerData;
|
||||
NTSTATUS Status;
|
||||
MIXER_STATUS MixerStatus;
|
||||
|
||||
MixerData = MMixerGetDataByDeviceId(MixerList, DeviceId);
|
||||
if (!MixerData)
|
||||
return MM_STATUS_INVALID_PARAMETER;
|
||||
|
||||
/* allocate pin connect */
|
||||
PinConnect = MMixerAllocatePinConnect(MixerContext, sizeof(KSDATAFORMAT_WAVEFORMATEX));
|
||||
if (!PinConnect)
|
||||
{
|
||||
/* no memory */
|
||||
return MM_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* initialize pin connect struct */
|
||||
MMixerInitializePinConnect(PinConnect, PinId);
|
||||
|
||||
/* get offset to dataformat */
|
||||
DataFormat = (PKSDATAFORMAT_WAVEFORMATEX) (PinConnect + 1);
|
||||
/* initialize with requested wave format */
|
||||
MMixerInitializeDataFormat(DataFormat, WaveFormatEx);
|
||||
|
||||
if (CreateCallback)
|
||||
{
|
||||
/* let the callback handle the creation */
|
||||
MixerStatus = CreateCallback(Context, DeviceId, PinId, MixerData->hDevice, PinConnect, DesiredAccess, PinHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* now create the pin */
|
||||
Status = KsCreatePin(MixerData->hDevice, PinConnect, DesiredAccess, PinHandle);
|
||||
|
||||
/* normalize status */
|
||||
if (Status == STATUS_SUCCESS)
|
||||
MixerStatus = MM_STATUS_SUCCESS;
|
||||
else
|
||||
MixerStatus = MM_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
/* free create info */
|
||||
MixerContext->Free(PinConnect);
|
||||
|
||||
/* done */
|
||||
return MixerStatus;
|
||||
}
|
||||
|
||||
VOID
|
||||
MMixerCheckFormat(
|
||||
IN PKSDATARANGE_AUDIO DataRangeAudio,
|
||||
IN LPWAVE_INFO WaveInfo,
|
||||
IN ULONG bInput)
|
||||
{
|
||||
ULONG Index, SampleFrequency;
|
||||
ULONG Result = 0;
|
||||
|
||||
for(Index = 0; Index < AUDIO_TEST_RANGE; Index++)
|
||||
{
|
||||
SampleFrequency = TestRange[Index].SampleRate;
|
||||
|
||||
if (DataRangeAudio->MinimumSampleFrequency <= SampleFrequency && DataRangeAudio->MaximumSampleFrequency >= SampleFrequency)
|
||||
{
|
||||
/* the audio adapter supports the sample frequency */
|
||||
if (DataRangeAudio->MinimumBitsPerSample <= 8 && DataRangeAudio->MaximumBitsPerSample >= 8)
|
||||
{
|
||||
Result |= TestRange[Index].Bit8Mono;
|
||||
|
||||
if (DataRangeAudio->MaximumChannels > 1)
|
||||
{
|
||||
/* check if pin supports the sample rate in 8-Bit Stereo */
|
||||
Result |= TestRange[Index].Bit8Stereo;
|
||||
}
|
||||
}
|
||||
|
||||
if (DataRangeAudio->MinimumBitsPerSample <= 16 && DataRangeAudio->MaximumBitsPerSample >= 16)
|
||||
{
|
||||
/* check if pin supports the sample rate in 16-Bit Mono */
|
||||
Result |= TestRange[Index].Bit16Mono;
|
||||
|
||||
|
||||
if (DataRangeAudio->MaximumChannels > 1)
|
||||
{
|
||||
/* check if pin supports the sample rate in 16-Bit Stereo */
|
||||
Result |= TestRange[Index].Bit16Stereo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (bInput)
|
||||
WaveInfo->u.InCaps.dwFormats = Result;
|
||||
else
|
||||
WaveInfo->u.OutCaps.dwFormats = Result;
|
||||
|
||||
DPRINT("Format %lx bInput %u\n", Result, bInput);
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerInitializeWaveInfo(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN PMIXER_LIST MixerList,
|
||||
IN LPMIXER_DATA MixerData,
|
||||
IN LPWSTR DeviceName,
|
||||
IN ULONG bWaveIn,
|
||||
IN ULONG PinId)
|
||||
{
|
||||
MIXER_STATUS Status;
|
||||
PKSMULTIPLE_ITEM MultipleItem;
|
||||
PKSDATARANGE_AUDIO DataRangeAudio;
|
||||
LPWAVE_INFO WaveInfo;
|
||||
|
||||
WaveInfo = (LPWAVE_INFO)MixerContext->Alloc(sizeof(WAVE_INFO));
|
||||
if (!WaveInfo)
|
||||
return MM_STATUS_NO_MEMORY;
|
||||
|
||||
/* initialize wave info */
|
||||
WaveInfo->DeviceId = MixerData->DeviceId;
|
||||
WaveInfo->PinId = PinId;
|
||||
|
||||
// sanity check
|
||||
ASSERT(wcslen(DeviceName) < MAXPNAMELEN);
|
||||
|
||||
/* copy device name */
|
||||
if (bWaveIn)
|
||||
{
|
||||
wcscpy(WaveInfo->u.InCaps.szPname, DeviceName);
|
||||
}
|
||||
else
|
||||
{
|
||||
wcscpy(WaveInfo->u.OutCaps.szPname, DeviceName);
|
||||
}
|
||||
|
||||
/* FIXME determine manufacturer / product id */
|
||||
if (bWaveIn)
|
||||
{
|
||||
WaveInfo->u.InCaps.wMid = MM_MICROSOFT;
|
||||
WaveInfo->u.InCaps.wPid = MM_PID_UNMAPPED;
|
||||
WaveInfo->u.InCaps.vDriverVersion = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
WaveInfo->u.OutCaps.wMid = MM_MICROSOFT;
|
||||
WaveInfo->u.OutCaps.wPid = MM_PID_UNMAPPED;
|
||||
WaveInfo->u.OutCaps.vDriverVersion = 1;
|
||||
}
|
||||
|
||||
/* get audio pin data ranges */
|
||||
Status = MMixerGetAudioPinDataRanges(MixerContext, MixerData->hDevice, PinId, &MultipleItem);
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
/* failed to get audio pin data ranges */
|
||||
MixerContext->Free(WaveInfo);
|
||||
return MM_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
/* find an KSDATARANGE_AUDIO range */
|
||||
Status = MMixerFindAudioDataRange(MultipleItem, &DataRangeAudio);
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
/* failed to find audio pin data range */
|
||||
MixerContext->Free(MultipleItem);
|
||||
MixerContext->Free(WaveInfo);
|
||||
return MM_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
/* store channel count */
|
||||
if (bWaveIn)
|
||||
{
|
||||
WaveInfo->u.InCaps.wChannels = DataRangeAudio->MaximumChannels;
|
||||
}
|
||||
else
|
||||
{
|
||||
WaveInfo->u.OutCaps.wChannels = DataRangeAudio->MaximumChannels;
|
||||
}
|
||||
|
||||
/* get all supported formats */
|
||||
MMixerCheckFormat(DataRangeAudio, WaveInfo, bWaveIn);
|
||||
|
||||
/* free dataranges buffer */
|
||||
MixerContext->Free(MultipleItem);
|
||||
|
||||
if (bWaveIn)
|
||||
{
|
||||
InsertTailList(&MixerList->WaveInList, &WaveInfo->Entry);
|
||||
MixerList->WaveInListCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
InsertTailList(&MixerList->WaveOutList, &WaveInfo->Entry);
|
||||
MixerList->WaveOutListCount++;
|
||||
}
|
||||
|
||||
return MM_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerOpenWave(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN ULONG DeviceIndex,
|
||||
IN ULONG bWaveIn,
|
||||
IN LPWAVEFORMATEX WaveFormat,
|
||||
IN PIN_CREATE_CALLBACK CreateCallback,
|
||||
IN PVOID Context,
|
||||
OUT PHANDLE PinHandle)
|
||||
{
|
||||
PMIXER_LIST MixerList;
|
||||
MIXER_STATUS Status;
|
||||
LPWAVE_INFO WaveInfo;
|
||||
ACCESS_MASK DesiredAccess = 0;
|
||||
|
||||
// verify mixer context
|
||||
Status = MMixerVerifyContext(MixerContext);
|
||||
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
// invalid context passed
|
||||
return Status;
|
||||
}
|
||||
|
||||
// grab mixer list
|
||||
MixerList = (PMIXER_LIST)MixerContext->MixerContext;
|
||||
|
||||
if (WaveFormat->wFormatTag != WAVE_FORMAT_PCM)
|
||||
{
|
||||
// not implemented
|
||||
return MM_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
/* find destination wave */
|
||||
Status = MMixerGetWaveInfoByIndexAndType(MixerList, DeviceIndex, bWaveIn, &WaveInfo);
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
/* failed to find wave info */
|
||||
return MM_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* get desired access */
|
||||
if (bWaveIn)
|
||||
{
|
||||
DesiredAccess |= GENERIC_READ;
|
||||
}
|
||||
else
|
||||
{
|
||||
DesiredAccess |= GENERIC_WRITE;
|
||||
}
|
||||
|
||||
/* now try open the pin */
|
||||
return MMixerOpenWavePin(MixerContext, MixerList, WaveInfo->DeviceId, WaveInfo->PinId, WaveFormat, DesiredAccess, CreateCallback, Context, PinHandle);
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerWaveInCapabilities(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN ULONG DeviceIndex,
|
||||
OUT LPWAVEINCAPSW Caps)
|
||||
{
|
||||
PMIXER_LIST MixerList;
|
||||
MIXER_STATUS Status;
|
||||
LPWAVE_INFO WaveInfo;
|
||||
|
||||
// verify mixer context
|
||||
Status = MMixerVerifyContext(MixerContext);
|
||||
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
// invalid context passed
|
||||
return Status;
|
||||
}
|
||||
|
||||
// grab mixer list
|
||||
MixerList = (PMIXER_LIST)MixerContext->MixerContext;
|
||||
|
||||
/* find destination wave */
|
||||
Status = MMixerGetWaveInfoByIndexAndType(MixerList, DeviceIndex, TRUE, &WaveInfo);
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
/* failed to find wave info */
|
||||
return MM_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
//copy capabilities
|
||||
MixerContext->Copy(Caps, &WaveInfo->u.InCaps, sizeof(WAVEINCAPSW));
|
||||
|
||||
return MM_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerWaveOutCapabilities(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN ULONG DeviceIndex,
|
||||
OUT LPWAVEOUTCAPSW Caps)
|
||||
{
|
||||
PMIXER_LIST MixerList;
|
||||
MIXER_STATUS Status;
|
||||
LPWAVE_INFO WaveInfo;
|
||||
|
||||
// verify mixer context
|
||||
Status = MMixerVerifyContext(MixerContext);
|
||||
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
// invalid context passed
|
||||
return Status;
|
||||
}
|
||||
|
||||
// grab mixer list
|
||||
MixerList = (PMIXER_LIST)MixerContext->MixerContext;
|
||||
|
||||
/* find destination wave */
|
||||
Status = MMixerGetWaveInfoByIndexAndType(MixerList, DeviceIndex, FALSE, &WaveInfo);
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
/* failed to find wave info */
|
||||
return MM_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
//copy capabilities
|
||||
MixerContext->Copy(Caps, &WaveInfo->u.OutCaps, sizeof(WAVEOUTCAPSW));
|
||||
|
||||
return MM_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
ULONG
|
||||
MMixerGetWaveInCount(
|
||||
IN PMIXER_CONTEXT MixerContext)
|
||||
{
|
||||
PMIXER_LIST MixerList;
|
||||
MIXER_STATUS Status;
|
||||
|
||||
// verify mixer context
|
||||
Status = MMixerVerifyContext(MixerContext);
|
||||
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
// invalid context passed
|
||||
return 0;
|
||||
}
|
||||
|
||||
// grab mixer list
|
||||
MixerList = (PMIXER_LIST)MixerContext->MixerContext;
|
||||
|
||||
return MixerList->WaveInListCount;
|
||||
}
|
||||
|
||||
ULONG
|
||||
MMixerGetWaveOutCount(
|
||||
IN PMIXER_CONTEXT MixerContext)
|
||||
{
|
||||
PMIXER_LIST MixerList;
|
||||
MIXER_STATUS Status;
|
||||
|
||||
// verify mixer context
|
||||
Status = MMixerVerifyContext(MixerContext);
|
||||
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
// invalid context passed
|
||||
return 0;
|
||||
}
|
||||
|
||||
// grab mixer list
|
||||
MixerList = (PMIXER_LIST)MixerContext->MixerContext;
|
||||
|
||||
return MixerList->WaveOutListCount;
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerSetWaveStatus(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN HANDLE PinHandle,
|
||||
IN KSSTATE State)
|
||||
{
|
||||
KSPROPERTY Property;
|
||||
ULONG Length;
|
||||
|
||||
/* setup property request */
|
||||
Property.Set = KSPROPSETID_Connection;
|
||||
Property.Id = KSPROPERTY_CONNECTION_STATE;
|
||||
Property.Flags = KSPROPERTY_TYPE_SET;
|
||||
|
||||
return MixerContext->Control(PinHandle, IOCTL_KS_PROPERTY, &Property, sizeof(KSPROPERTY), &State, sizeof(KSSTATE), &Length);
|
||||
}
|
||||
|
||||
MIXER_STATUS
|
||||
MMixerGetWaveDevicePath(
|
||||
IN PMIXER_CONTEXT MixerContext,
|
||||
IN ULONG bWaveIn,
|
||||
IN ULONG DeviceId,
|
||||
OUT LPWSTR * DevicePath)
|
||||
{
|
||||
PMIXER_LIST MixerList;
|
||||
LPMIXER_DATA MixerData;
|
||||
LPWAVE_INFO WaveInfo;
|
||||
ULONG Length;
|
||||
MIXER_STATUS Status;
|
||||
|
||||
// verify mixer context
|
||||
Status = MMixerVerifyContext(MixerContext);
|
||||
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
// invalid context passed
|
||||
return Status;
|
||||
}
|
||||
|
||||
// grab mixer list
|
||||
MixerList = (PMIXER_LIST)MixerContext->MixerContext;
|
||||
|
||||
/* find destination wave */
|
||||
Status = MMixerGetWaveInfoByIndexAndType(MixerList, DeviceId, bWaveIn, &WaveInfo);
|
||||
if (Status != MM_STATUS_SUCCESS)
|
||||
{
|
||||
/* failed to find wave info */
|
||||
return MM_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* get associated device id */
|
||||
MixerData = MMixerGetDataByDeviceId(MixerList, WaveInfo->DeviceId);
|
||||
if (!MixerData)
|
||||
return MM_STATUS_INVALID_PARAMETER;
|
||||
|
||||
/* calculate length */
|
||||
Length = wcslen(MixerData->DeviceName)+1;
|
||||
|
||||
/* allocate destination buffer */
|
||||
*DevicePath = MixerContext->Alloc(Length * sizeof(WCHAR));
|
||||
|
||||
if (!*DevicePath)
|
||||
{
|
||||
/* no memory */
|
||||
return MM_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* copy device path */
|
||||
MixerContext->Copy(*DevicePath, MixerData->DeviceName, Length * sizeof(WCHAR));
|
||||
|
||||
/* done */
|
||||
return MM_STATUS_SUCCESS;
|
||||
}
|
||||
|
9
lib/drivers/sound/shared/shared.rbuild
Normal file
9
lib/drivers/sound/shared/shared.rbuild
Normal file
|
@ -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>
|
49
lib/drivers/sound/shared/time.c
Normal file
49
lib/drivers/sound/shared/time.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
25
lib/drivers/sound/sound.rbuild
Normal file
25
lib/drivers/sound/sound.rbuild
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?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="mmixer">
|
||||
<xi:include href="mmixer/mmixer.rbuild" />
|
||||
</directory>
|
||||
<directory name="mmebuddy">
|
||||
<xi:include href="mmebuddy/mmebuddy.rbuild" />
|
||||
</directory>
|
||||
<directory name="mment4">
|
||||
<xi:include href="mment4/mment4.rbuild" />
|
||||
</directory>
|
||||
</group>
|
147
lib/drivers/sound/soundblaster/dsp_io.c
Normal file
147
lib/drivers/sound/soundblaster/dsp_io.c
Normal file
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
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;
|
||||
BOOLEAN DataAvailable = FALSE;
|
||||
|
||||
/* Should be called from DriverEntry with this IRQL */
|
||||
ASSERT(KeGetCurrentIrql() == 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;
|
||||
}
|
246
lib/drivers/sound/soundblaster/mixer.c
Normal file
246
lib/drivers/sound/soundblaster/mixer.c
Normal file
|
@ -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);
|
||||
}
|
154
lib/drivers/sound/soundblaster/rate.c
Normal file
154
lib/drivers/sound/soundblaster/rate.c
Normal file
|
@ -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);
|
||||
}
|
||||
|
13
lib/drivers/sound/soundblaster/soundblaster.rbuild
Normal file
13
lib/drivers/sound/soundblaster/soundblaster.rbuild
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE module SYSTEM "../../../../tools/rbuild/project.dtd">
|
||||
<module name="soundblaster" type="staticlibrary">
|
||||
<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>
|
66
lib/drivers/sound/soundblaster/speaker.c
Normal file
66
lib/drivers/sound/soundblaster/speaker.c
Normal file
|
@ -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;
|
||||
}
|
50
lib/drivers/sound/soundblaster/version.c
Normal file
50
lib/drivers/sound/soundblaster/version.c
Normal file
|
@ -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;
|
||||
}
|
93
lib/drivers/sound/uartmidi/midiuart.c
Normal file
93
lib/drivers/sound/uartmidi/midiuart.c
Normal file
|
@ -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;
|
||||
}
|
9
lib/drivers/sound/uartmidi/uartmidi.rbuild
Normal file
9
lib/drivers/sound/uartmidi/uartmidi.rbuild
Normal file
|
@ -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>
|
Loading…
Add table
Add a link
Reference in a new issue