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