Create a branch for cmake bringup.

svn path=/branches/cmake-bringup/; revision=48236
This commit is contained in:
Amine Khaldi 2010-07-24 18:52:44 +00:00
parent a28e798006
commit c424146e2c
20602 changed files with 0 additions and 1140137 deletions

View 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);
}

View 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;
}

View 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>

View 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;
}

View 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);
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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>

View 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;
}

View 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

View 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]);
}

View 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;
}

View 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;
}

View 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);
}

View 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);
}

View 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);
}

View 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;
}

View 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;
}

View 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

View 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;
}

View 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>

View 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;
}

File diff suppressed because it is too large Load diff

View 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;
}

View 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;
}

View 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);

View 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>

View 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);

View 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;
}

View 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;
}

View 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>

View 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;
}

View 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>

View 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;
}

View 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);
}

View 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);
}

View 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>

View 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;
}

View 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;
}

View 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;
}

View 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>