mirror of
https://github.com/reactos/reactos.git
synced 2024-09-30 06:27:38 +00:00
6fa8c2ac56
By taking alternative code-paths in WdmAud and bypassing Sysaudio. This is a squashed backport of: 0.4.15-dev-791-g6d7ebc2048
the USE_MMIXER_LIB which gives the best results when paired with those 2 previous patches: 0.4.15-dev-765-gb8e936a57b
CORE-17214 (#3148) wdmaud-racecondition-fix and 0.4.15-dev-796-ga27f0debca
CORE-17276 winmm:mixer-testbot-crash-fix Defining USE_MMIXER_LIB will fix/improve: - the test execution times of "GCCLin_x86 on Test VBox" will be dramatically improve (iirc by ~10-15min) - CORE-8726/CORE-9986/CORE-16564 AC97 driver from rapps will work in the same session that the driver is installed, not a single reboot is needed anymore - CORE-13202 Unhandled exception from wdmaud.drv when recording sound in Scratch 1.4 leads to app-crash (gets fixed even for older builds that did not receive 0.4.15-dev-2794-g81f8bce
yet) - CORE-13488 A deadlock in "DiabloII" character selection screen and "The Lion King II" - CORE-9981 "DosBox + Commander Keen6" almost 100% fixed, DosBox + Commander Keen6 properly plays music instead of garbled output, same improvement for "ScummVM 2.0 with Monkey Island 2" The playback is not yet *entirely* perfect, still a few hiccups now and then, but by orders of magnitude better than before. Most of the above mentioned issues were a regression of SVN r44721 ==4b9731d0db
Defining USE_MMIXER_LIB will also have some negative aspects: - CORE-17277 crash of dsound:duplex on "GCCLin_x86 on Test VBox" gets unhidden on the bot, but was proven to be broken even beforehand already. The driver beforehand was just not found and the tests were skipped therefore. - CORE-17278 crash of dsound:capture on "GCCLin_x86 on Test VBox" gets unhidden on the bot, but was proven to be broken even beforehand already. The driver beforehand was just not found and the tests were skipped therefore. - CORE-17744 Fox Audio Player 0.10.2 can not longer play an mp3 file with 22kHz, WMM audio output - It may also have a negative impact for CORE-17285 "Realtek HD Audio" but Oleg Dubinsky accepted to tolerate that and aims to approach it differently. We resolved that as WontFix therefore.
870 lines
23 KiB
C
870 lines
23 KiB
C
/*
|
|
* PROJECT: ReactOS Sound System
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: dll/win32/wdmaud.drv/mmixer.c
|
|
*
|
|
* PURPOSE: WDM Audio Mixer API (User-mode part)
|
|
* PROGRAMMERS: Johannes Anderwald
|
|
*/
|
|
|
|
#include "wdmaud.h"
|
|
|
|
#include <winreg.h>
|
|
#include <setupapi.h>
|
|
#include <mmixer.h>
|
|
#define NTOS_MODE_USER
|
|
#include <ndk/rtlfuncs.h>
|
|
#include <ndk/iofuncs.h>
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
#include <mmebuddy_debug.h>
|
|
|
|
|
|
BOOL MMixerLibraryInitialized = FALSE;
|
|
|
|
|
|
|
|
PVOID Alloc(ULONG NumBytes);
|
|
MIXER_STATUS Close(HANDLE hDevice);
|
|
VOID Free(PVOID Block);
|
|
VOID Copy(PVOID Src, PVOID Dst, ULONG NumBytes);
|
|
MIXER_STATUS Open(IN LPWSTR DevicePath, OUT PHANDLE hDevice);
|
|
MIXER_STATUS Control(IN HANDLE hMixer, IN ULONG dwIoControlCode, IN PVOID lpInBuffer, IN ULONG nInBufferSize, OUT PVOID lpOutBuffer, ULONG nOutBufferSize, PULONG lpBytesReturned);
|
|
MIXER_STATUS Enum(IN PVOID EnumContext, IN ULONG DeviceIndex, OUT LPWSTR * DeviceName, OUT PHANDLE OutHandle, OUT PHANDLE OutKey);
|
|
MIXER_STATUS OpenKey(IN HANDLE hKey, IN LPWSTR SubKey, IN ULONG DesiredAccess, OUT PHANDLE OutKey);
|
|
MIXER_STATUS CloseKey(IN HANDLE hKey);
|
|
MIXER_STATUS QueryKeyValue(IN HANDLE hKey, IN LPWSTR KeyName, OUT PVOID * ResultBuffer, OUT PULONG ResultLength, OUT PULONG KeyType);
|
|
PVOID AllocEventData(IN ULONG ExtraSize);
|
|
VOID FreeEventData(IN PVOID EventData);
|
|
|
|
MIXER_CONTEXT MixerContext =
|
|
{
|
|
sizeof(MIXER_CONTEXT),
|
|
NULL,
|
|
Alloc,
|
|
Control,
|
|
Free,
|
|
Open,
|
|
Close,
|
|
Copy,
|
|
OpenKey,
|
|
QueryKeyValue,
|
|
CloseKey,
|
|
AllocEventData,
|
|
FreeEventData
|
|
};
|
|
|
|
GUID CategoryGuid = {STATIC_KSCATEGORY_AUDIO};
|
|
|
|
MIXER_STATUS
|
|
QueryKeyValue(
|
|
IN HANDLE hKey,
|
|
IN LPWSTR KeyName,
|
|
OUT PVOID * ResultBuffer,
|
|
OUT PULONG ResultLength,
|
|
OUT PULONG KeyType)
|
|
{
|
|
if (RegQueryValueExW((HKEY)hKey, KeyName, NULL, KeyType, NULL, ResultLength) == ERROR_FILE_NOT_FOUND)
|
|
return MM_STATUS_UNSUCCESSFUL;
|
|
|
|
*ResultBuffer = HeapAlloc(GetProcessHeap(), 0, *ResultLength);
|
|
if (*ResultBuffer == NULL)
|
|
return MM_STATUS_NO_MEMORY;
|
|
|
|
if (RegQueryValueExW((HKEY)hKey, KeyName, NULL, KeyType, *ResultBuffer, ResultLength) != ERROR_SUCCESS)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, *ResultBuffer);
|
|
return MM_STATUS_UNSUCCESSFUL;
|
|
}
|
|
return MM_STATUS_SUCCESS;
|
|
}
|
|
|
|
MIXER_STATUS
|
|
OpenKey(
|
|
IN HANDLE hKey,
|
|
IN LPWSTR SubKey,
|
|
IN ULONG DesiredAccess,
|
|
OUT PHANDLE OutKey)
|
|
{
|
|
if (RegOpenKeyExW((HKEY)hKey, SubKey, 0, DesiredAccess, (PHKEY)OutKey) == ERROR_SUCCESS)
|
|
return MM_STATUS_SUCCESS;
|
|
|
|
return MM_STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
MIXER_STATUS
|
|
CloseKey(
|
|
IN HANDLE hKey)
|
|
{
|
|
RegCloseKey((HKEY)hKey);
|
|
return MM_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
PVOID Alloc(ULONG NumBytes)
|
|
{
|
|
return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, NumBytes);
|
|
}
|
|
|
|
MIXER_STATUS
|
|
Close(HANDLE hDevice)
|
|
{
|
|
if (CloseHandle(hDevice))
|
|
return MM_STATUS_SUCCESS;
|
|
else
|
|
return MM_STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
VOID
|
|
Free(PVOID Block)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, Block);
|
|
}
|
|
|
|
VOID
|
|
Copy(PVOID Src, PVOID Dst, ULONG NumBytes)
|
|
{
|
|
RtlMoveMemory(Src, Dst, NumBytes);
|
|
}
|
|
|
|
MIXER_STATUS
|
|
Open(
|
|
IN LPWSTR DevicePath,
|
|
OUT PHANDLE hDevice)
|
|
{
|
|
DevicePath[1] = L'\\';
|
|
*hDevice = CreateFileW(DevicePath,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_OVERLAPPED,
|
|
NULL);
|
|
if (*hDevice == INVALID_HANDLE_VALUE)
|
|
{
|
|
return MM_STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
return MM_STATUS_SUCCESS;
|
|
}
|
|
|
|
MIXER_STATUS
|
|
Control(
|
|
IN HANDLE hMixer,
|
|
IN ULONG dwIoControlCode,
|
|
IN PVOID lpInBuffer,
|
|
IN ULONG nInBufferSize,
|
|
OUT PVOID lpOutBuffer,
|
|
ULONG nOutBufferSize,
|
|
PULONG lpBytesReturned)
|
|
{
|
|
OVERLAPPED Overlapped;
|
|
BOOLEAN IoResult;
|
|
DWORD Transferred = 0;
|
|
|
|
/* 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 MM_STATUS_NO_MEMORY;
|
|
|
|
/* Talk to the device */
|
|
IoResult = DeviceIoControl(hMixer,
|
|
dwIoControlCode,
|
|
lpInBuffer,
|
|
nInBufferSize,
|
|
lpOutBuffer,
|
|
nOutBufferSize,
|
|
&Transferred,
|
|
&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);
|
|
|
|
if (GetLastError() == ERROR_MORE_DATA || GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
if ( lpBytesReturned )
|
|
*lpBytesReturned = Transferred;
|
|
return MM_STATUS_MORE_ENTRIES;
|
|
}
|
|
|
|
return MM_STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
/* Wait for the I/O to complete */
|
|
IoResult = GetOverlappedResult(hMixer,
|
|
&Overlapped,
|
|
&Transferred,
|
|
TRUE);
|
|
|
|
/* Don't need this any more */
|
|
CloseHandle(Overlapped.hEvent);
|
|
|
|
if ( ! IoResult )
|
|
return MM_STATUS_UNSUCCESSFUL;
|
|
|
|
if ( lpBytesReturned )
|
|
*lpBytesReturned = Transferred;
|
|
|
|
return MM_STATUS_SUCCESS;
|
|
}
|
|
|
|
MIXER_STATUS
|
|
Enum(
|
|
IN PVOID EnumContext,
|
|
IN ULONG DeviceIndex,
|
|
OUT LPWSTR * DeviceName,
|
|
OUT PHANDLE OutHandle,
|
|
OUT PHANDLE OutKey)
|
|
{
|
|
SP_DEVICE_INTERFACE_DATA InterfaceData;
|
|
SP_DEVINFO_DATA DeviceData;
|
|
PSP_DEVICE_INTERFACE_DETAIL_DATA_W DetailData;
|
|
BOOL Result;
|
|
DWORD Length;
|
|
MIXER_STATUS Status;
|
|
|
|
//printf("Enum EnumContext %p DeviceIndex %lu OutHandle %p\n", EnumContext, DeviceIndex, OutHandle);
|
|
|
|
InterfaceData.cbSize = sizeof(InterfaceData);
|
|
InterfaceData.Reserved = 0;
|
|
|
|
Result = SetupDiEnumDeviceInterfaces(EnumContext,
|
|
NULL,
|
|
&CategoryGuid,
|
|
DeviceIndex,
|
|
&InterfaceData);
|
|
|
|
if (!Result)
|
|
{
|
|
if (GetLastError() == ERROR_NO_MORE_ITEMS)
|
|
{
|
|
return MM_STATUS_NO_MORE_DEVICES;
|
|
}
|
|
return MM_STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
Length = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) + MAX_PATH * sizeof(WCHAR);
|
|
DetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA_W)HeapAlloc(GetProcessHeap(),
|
|
0,
|
|
Length);
|
|
DetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
|
|
DeviceData.cbSize = sizeof(DeviceData);
|
|
DeviceData.Reserved = 0;
|
|
|
|
Result = SetupDiGetDeviceInterfaceDetailW(EnumContext,
|
|
&InterfaceData,
|
|
DetailData,
|
|
Length,
|
|
NULL,
|
|
&DeviceData);
|
|
|
|
if (!Result)
|
|
{
|
|
DPRINT("SetupDiGetDeviceInterfaceDetailW failed with %lu\n", GetLastError());
|
|
return MM_STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
|
|
*OutKey = SetupDiOpenDeviceInterfaceRegKey(EnumContext, &InterfaceData, 0, KEY_READ);
|
|
if ((HKEY)*OutKey == INVALID_HANDLE_VALUE)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, DetailData);
|
|
return MM_STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
Status = Open(DetailData->DevicePath, OutHandle);
|
|
|
|
if (Status != MM_STATUS_SUCCESS)
|
|
{
|
|
RegCloseKey((HKEY)*OutKey);
|
|
HeapFree(GetProcessHeap(), 0, DetailData);
|
|
return Status;
|
|
}
|
|
|
|
*DeviceName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(DetailData->DevicePath)+1) * sizeof(WCHAR));
|
|
if (*DeviceName == NULL)
|
|
{
|
|
CloseHandle(*OutHandle);
|
|
RegCloseKey((HKEY)*OutKey);
|
|
HeapFree(GetProcessHeap(), 0, DetailData);
|
|
return MM_STATUS_NO_MEMORY;
|
|
}
|
|
wcscpy(*DeviceName, DetailData->DevicePath);
|
|
HeapFree(GetProcessHeap(), 0, DetailData);
|
|
|
|
return Status;
|
|
}
|
|
|
|
PVOID
|
|
AllocEventData(
|
|
IN ULONG ExtraSize)
|
|
{
|
|
PKSEVENTDATA Data = (PKSEVENTDATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(KSEVENTDATA) + ExtraSize);
|
|
if (!Data)
|
|
return NULL;
|
|
|
|
Data->EventHandle.Event = CreateEventW(NULL, FALSE, FALSE, NULL);
|
|
if (!Data->EventHandle.Event)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, Data);
|
|
return NULL;
|
|
}
|
|
|
|
Data->NotificationType = KSEVENTF_EVENT_HANDLE;
|
|
return Data;
|
|
}
|
|
|
|
VOID
|
|
FreeEventData(IN PVOID EventData)
|
|
{
|
|
PKSEVENTDATA Data = (PKSEVENTDATA)EventData;
|
|
|
|
CloseHandle(Data->EventHandle.Event);
|
|
HeapFree(GetProcessHeap(), 0, Data);
|
|
}
|
|
|
|
|
|
BOOL
|
|
WdmAudInitUserModeMixer()
|
|
{
|
|
HDEVINFO DeviceHandle;
|
|
MIXER_STATUS Status;
|
|
|
|
if (MMixerLibraryInitialized)
|
|
{
|
|
/* library is already initialized */
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* create a device list */
|
|
DeviceHandle = SetupDiGetClassDevs(&CategoryGuid,
|
|
NULL,
|
|
NULL,
|
|
DIGCF_DEVICEINTERFACE/* FIXME |DIGCF_PRESENT*/);
|
|
|
|
if (DeviceHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
/* failed to create a device list */
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/* initialize the mixer library */
|
|
Status = MMixerInitialize(&MixerContext, Enum, (PVOID)DeviceHandle);
|
|
|
|
/* free device list */
|
|
SetupDiDestroyDeviceInfoList(DeviceHandle);
|
|
|
|
if (Status != MM_STATUS_SUCCESS)
|
|
{
|
|
/* failed to initialize mixer library */
|
|
DPRINT1("Failed to initialize mixer library with %x\n", Status);
|
|
return FALSE;
|
|
}
|
|
|
|
/* library is now initialized */
|
|
MMixerLibraryInitialized = TRUE;
|
|
|
|
/* completed successfully */
|
|
return TRUE;
|
|
}
|
|
|
|
MMRESULT
|
|
WdmAudCleanupByMMixer()
|
|
{
|
|
/* TODO */
|
|
return MMSYSERR_NOERROR;
|
|
}
|
|
|
|
MMRESULT
|
|
WdmAudGetMixerCapabilities(
|
|
IN ULONG DeviceId,
|
|
LPMIXERCAPSW Capabilities)
|
|
{
|
|
if (MMixerGetCapabilities(&MixerContext, DeviceId, Capabilities) == MM_STATUS_SUCCESS)
|
|
return MMSYSERR_NOERROR;
|
|
|
|
return MMSYSERR_BADDEVICEID;
|
|
}
|
|
|
|
MMRESULT
|
|
WdmAudGetLineInfo(
|
|
IN HANDLE hMixer,
|
|
IN DWORD MixerId,
|
|
IN LPMIXERLINEW MixLine,
|
|
IN ULONG Flags)
|
|
{
|
|
if (MMixerGetLineInfo(&MixerContext, hMixer, MixerId, Flags, MixLine) == MM_STATUS_SUCCESS)
|
|
return MMSYSERR_NOERROR;
|
|
|
|
return MMSYSERR_ERROR;
|
|
}
|
|
|
|
MMRESULT
|
|
WdmAudGetLineControls(
|
|
IN HANDLE hMixer,
|
|
IN DWORD MixerId,
|
|
IN LPMIXERLINECONTROLSW MixControls,
|
|
IN ULONG Flags)
|
|
{
|
|
if (MMixerGetLineControls(&MixerContext, hMixer, MixerId, Flags, MixControls) == MM_STATUS_SUCCESS)
|
|
return MMSYSERR_NOERROR;
|
|
|
|
return MMSYSERR_ERROR;
|
|
}
|
|
|
|
MMRESULT
|
|
WdmAudSetControlDetails(
|
|
IN HANDLE hMixer,
|
|
IN DWORD MixerId,
|
|
IN LPMIXERCONTROLDETAILS MixDetails,
|
|
IN ULONG Flags)
|
|
{
|
|
if (MMixerSetControlDetails(&MixerContext, hMixer, MixerId, Flags, MixDetails) == MM_STATUS_SUCCESS)
|
|
return MMSYSERR_NOERROR;
|
|
|
|
return MMSYSERR_ERROR;
|
|
|
|
}
|
|
|
|
MMRESULT
|
|
WdmAudGetControlDetails(
|
|
IN HANDLE hMixer,
|
|
IN DWORD MixerId,
|
|
IN LPMIXERCONTROLDETAILS MixDetails,
|
|
IN ULONG Flags)
|
|
{
|
|
if (MMixerGetControlDetails(&MixerContext, hMixer, MixerId, Flags, MixDetails) == MM_STATUS_SUCCESS)
|
|
return MMSYSERR_NOERROR;
|
|
|
|
return MMSYSERR_ERROR;
|
|
}
|
|
|
|
MMRESULT
|
|
WdmAudGetWaveOutCapabilities(
|
|
IN ULONG DeviceId,
|
|
LPWAVEOUTCAPSW Capabilities)
|
|
{
|
|
if (MMixerWaveOutCapabilities(&MixerContext, DeviceId, Capabilities) == MM_STATUS_SUCCESS)
|
|
return MMSYSERR_NOERROR;
|
|
|
|
return MMSYSERR_ERROR;
|
|
|
|
}
|
|
|
|
MMRESULT
|
|
WdmAudGetWaveInCapabilities(
|
|
IN ULONG DeviceId,
|
|
LPWAVEINCAPSW Capabilities)
|
|
{
|
|
if (MMixerWaveInCapabilities(&MixerContext, DeviceId, Capabilities) == MM_STATUS_SUCCESS)
|
|
return MMSYSERR_NOERROR;
|
|
|
|
return MMSYSERR_ERROR;
|
|
}
|
|
|
|
MMRESULT
|
|
WdmAudSetWaveDeviceFormatByMMixer(
|
|
IN PSOUND_DEVICE_INSTANCE Instance,
|
|
IN DWORD DeviceId,
|
|
IN PWAVEFORMATEX WaveFormat,
|
|
IN DWORD WaveFormatSize)
|
|
{
|
|
MMDEVICE_TYPE DeviceType;
|
|
PSOUND_DEVICE SoundDevice;
|
|
MMRESULT Result;
|
|
BOOL bWaveIn;
|
|
|
|
Result = GetSoundDeviceFromInstance(Instance, &SoundDevice);
|
|
|
|
if ( ! MMSUCCESS(Result) )
|
|
{
|
|
return TranslateInternalMmResult(Result);
|
|
}
|
|
|
|
Result = GetSoundDeviceType(SoundDevice, &DeviceType);
|
|
SND_ASSERT( Result == MMSYSERR_NOERROR );
|
|
|
|
bWaveIn = (DeviceType == WAVE_IN_DEVICE_TYPE ? TRUE : FALSE);
|
|
|
|
if (MMixerOpenWave(&MixerContext, DeviceId, bWaveIn, WaveFormat, NULL, NULL, &Instance->Handle) == MM_STATUS_SUCCESS)
|
|
{
|
|
if (DeviceType == WAVE_OUT_DEVICE_TYPE)
|
|
{
|
|
MMixerSetWaveStatus(&MixerContext, Instance->Handle, KSSTATE_ACQUIRE);
|
|
MMixerSetWaveStatus(&MixerContext, Instance->Handle, KSSTATE_PAUSE);
|
|
MMixerSetWaveStatus(&MixerContext, Instance->Handle, KSSTATE_RUN);
|
|
}
|
|
return MMSYSERR_NOERROR;
|
|
}
|
|
return MMSYSERR_ERROR;
|
|
}
|
|
|
|
|
|
MMRESULT
|
|
WdmAudGetCapabilitiesByMMixer(
|
|
IN PSOUND_DEVICE SoundDevice,
|
|
IN DWORD DeviceId,
|
|
OUT PVOID Capabilities,
|
|
IN DWORD CapabilitiesSize)
|
|
{
|
|
MMDEVICE_TYPE DeviceType;
|
|
MMRESULT Result;
|
|
|
|
Result = GetSoundDeviceType(SoundDevice, &DeviceType);
|
|
SND_ASSERT( Result == MMSYSERR_NOERROR );
|
|
|
|
if (DeviceType == MIXER_DEVICE_TYPE)
|
|
{
|
|
return WdmAudGetMixerCapabilities(DeviceId, (LPMIXERCAPSW)Capabilities);
|
|
}
|
|
else if (DeviceType == WAVE_OUT_DEVICE_TYPE)
|
|
{
|
|
return WdmAudGetWaveOutCapabilities(DeviceId, (LPWAVEOUTCAPSW)Capabilities);
|
|
}
|
|
else if (DeviceType == WAVE_IN_DEVICE_TYPE)
|
|
{
|
|
return WdmAudGetWaveInCapabilities(DeviceId, (LPWAVEINCAPSW)Capabilities);
|
|
}
|
|
else
|
|
{
|
|
// not supported
|
|
return MMSYSERR_ERROR;
|
|
}
|
|
}
|
|
|
|
MMRESULT
|
|
WdmAudOpenSoundDeviceByMMixer(
|
|
IN struct _SOUND_DEVICE* SoundDevice,
|
|
OUT PVOID* Handle)
|
|
{
|
|
if (WdmAudInitUserModeMixer())
|
|
return MMSYSERR_NOERROR;
|
|
else
|
|
return MMSYSERR_ERROR;
|
|
}
|
|
|
|
MMRESULT
|
|
WdmAudCloseSoundDeviceByMMixer(
|
|
IN struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance,
|
|
IN PVOID Handle)
|
|
{
|
|
MMDEVICE_TYPE DeviceType;
|
|
PSOUND_DEVICE SoundDevice;
|
|
MMRESULT Result;
|
|
|
|
Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
|
|
|
|
if ( ! MMSUCCESS(Result) )
|
|
{
|
|
return TranslateInternalMmResult(Result);
|
|
}
|
|
|
|
Result = GetSoundDeviceType(SoundDevice, &DeviceType);
|
|
SND_ASSERT( Result == MMSYSERR_NOERROR );
|
|
|
|
if (DeviceType == MIXER_DEVICE_TYPE)
|
|
{
|
|
/* no op */
|
|
return MMSYSERR_NOERROR;
|
|
}
|
|
else if (DeviceType == WAVE_IN_DEVICE_TYPE || DeviceType == WAVE_OUT_DEVICE_TYPE)
|
|
{
|
|
/* make sure the pin is stopped */
|
|
MMixerSetWaveStatus(&MixerContext, SoundDeviceInstance->Handle, KSSTATE_PAUSE);
|
|
MMixerSetWaveStatus(&MixerContext, SoundDeviceInstance->Handle, KSSTATE_ACQUIRE);
|
|
MMixerSetWaveStatus(&MixerContext, SoundDeviceInstance->Handle, KSSTATE_STOP);
|
|
|
|
CloseHandle(Handle);
|
|
return MMSYSERR_NOERROR;
|
|
}
|
|
|
|
/* midi is not supported */
|
|
return MMSYSERR_ERROR;
|
|
}
|
|
|
|
MMRESULT
|
|
WdmAudGetNumWdmDevsByMMixer(
|
|
IN MMDEVICE_TYPE DeviceType,
|
|
OUT DWORD* DeviceCount)
|
|
{
|
|
switch(DeviceType)
|
|
{
|
|
case MIXER_DEVICE_TYPE:
|
|
*DeviceCount = MMixerGetCount(&MixerContext);
|
|
break;
|
|
case WAVE_OUT_DEVICE_TYPE:
|
|
*DeviceCount = MMixerGetWaveOutCount(&MixerContext);
|
|
break;
|
|
case WAVE_IN_DEVICE_TYPE:
|
|
*DeviceCount = MMixerGetWaveInCount(&MixerContext);
|
|
break;
|
|
default:
|
|
*DeviceCount = 0;
|
|
}
|
|
return MMSYSERR_NOERROR;
|
|
}
|
|
|
|
MMRESULT
|
|
WdmAudQueryMixerInfoByMMixer(
|
|
IN struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance,
|
|
IN DWORD MixerId,
|
|
IN UINT uMsg,
|
|
IN LPVOID Parameter,
|
|
IN DWORD Flags)
|
|
{
|
|
LPMIXERLINEW MixLine;
|
|
LPMIXERLINECONTROLSW MixControls;
|
|
LPMIXERCONTROLDETAILS MixDetails;
|
|
HANDLE hMixer = NULL;
|
|
|
|
MixLine = (LPMIXERLINEW)Parameter;
|
|
MixControls = (LPMIXERLINECONTROLSW)Parameter;
|
|
MixDetails = (LPMIXERCONTROLDETAILS)Parameter;
|
|
|
|
/* FIXME param checks */
|
|
|
|
if (SoundDeviceInstance)
|
|
{
|
|
hMixer = SoundDeviceInstance->Handle;
|
|
}
|
|
|
|
switch(uMsg)
|
|
{
|
|
case MXDM_GETLINEINFO:
|
|
return WdmAudGetLineInfo(hMixer, MixerId, MixLine, Flags);
|
|
case MXDM_GETLINECONTROLS:
|
|
return WdmAudGetLineControls(hMixer, MixerId, MixControls, Flags);
|
|
case MXDM_SETCONTROLDETAILS:
|
|
return WdmAudSetControlDetails(hMixer, MixerId, MixDetails, Flags);
|
|
case MXDM_GETCONTROLDETAILS:
|
|
return WdmAudGetControlDetails(hMixer, MixerId, MixDetails, Flags);
|
|
default:
|
|
DPRINT1("MixerId %lu, uMsg %lu, Parameter %p, Flags %lu\n", MixerId, uMsg, Parameter, Flags);
|
|
SND_ASSERT(0);
|
|
return MMSYSERR_NOTSUPPORTED;
|
|
}
|
|
}
|
|
|
|
MMRESULT
|
|
WdmAudGetDeviceInterfaceStringByMMixer(
|
|
IN MMDEVICE_TYPE DeviceType,
|
|
IN DWORD DeviceId,
|
|
IN LPWSTR Interface,
|
|
IN DWORD InterfaceLength,
|
|
OUT DWORD * InterfaceSize)
|
|
{
|
|
/* FIXME */
|
|
return MMSYSERR_NOTSUPPORTED;
|
|
}
|
|
|
|
VOID
|
|
CALLBACK
|
|
MixerEventCallback(
|
|
IN PVOID MixerEventContext,
|
|
IN HANDLE hMixer,
|
|
IN ULONG NotificationType,
|
|
IN ULONG Value)
|
|
{
|
|
PSOUND_DEVICE_INSTANCE Instance = (PSOUND_DEVICE_INSTANCE)MixerEventContext;
|
|
|
|
DriverCallback(Instance->WinMM.ClientCallback,
|
|
HIWORD(Instance->WinMM.Flags),
|
|
Instance->WinMM.Handle,
|
|
NotificationType,
|
|
Instance->WinMM.ClientCallbackInstanceData,
|
|
(DWORD_PTR)Value,
|
|
0);
|
|
}
|
|
|
|
MMRESULT
|
|
WdmAudSetMixerDeviceFormatByMMixer(
|
|
IN PSOUND_DEVICE_INSTANCE Instance,
|
|
IN DWORD DeviceId,
|
|
IN PWAVEFORMATEX WaveFormat,
|
|
IN DWORD WaveFormatSize)
|
|
{
|
|
if (MMixerOpen(&MixerContext, DeviceId, (PVOID)Instance, MixerEventCallback, &Instance->Handle) == MM_STATUS_SUCCESS)
|
|
return MMSYSERR_NOERROR;
|
|
|
|
return MMSYSERR_BADDEVICEID;
|
|
}
|
|
|
|
MMRESULT
|
|
WdmAudSetWaveStateByMMixer(
|
|
IN struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance,
|
|
IN BOOL bStart)
|
|
{
|
|
MMDEVICE_TYPE DeviceType;
|
|
PSOUND_DEVICE SoundDevice;
|
|
MMRESULT Result;
|
|
|
|
Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
|
|
SND_ASSERT( Result == MMSYSERR_NOERROR );
|
|
|
|
|
|
Result = GetSoundDeviceType(SoundDevice, &DeviceType);
|
|
SND_ASSERT( Result == MMSYSERR_NOERROR );
|
|
|
|
if (DeviceType == WAVE_IN_DEVICE_TYPE || DeviceType == WAVE_OUT_DEVICE_TYPE)
|
|
{
|
|
if (bStart)
|
|
{
|
|
MMixerSetWaveStatus(&MixerContext, SoundDeviceInstance->Handle, KSSTATE_ACQUIRE);
|
|
MMixerSetWaveStatus(&MixerContext, SoundDeviceInstance->Handle, KSSTATE_PAUSE);
|
|
MMixerSetWaveStatus(&MixerContext, SoundDeviceInstance->Handle, KSSTATE_RUN);
|
|
}
|
|
else
|
|
{
|
|
MMixerSetWaveStatus(&MixerContext, SoundDeviceInstance->Handle, KSSTATE_PAUSE);
|
|
MMixerSetWaveStatus(&MixerContext, SoundDeviceInstance->Handle, KSSTATE_ACQUIRE);
|
|
MMixerSetWaveStatus(&MixerContext, SoundDeviceInstance->Handle, KSSTATE_STOP);
|
|
}
|
|
}
|
|
else if (DeviceType == MIDI_IN_DEVICE_TYPE || DeviceType == MIDI_OUT_DEVICE_TYPE)
|
|
{
|
|
if (bStart)
|
|
{
|
|
MMixerSetMidiStatus(&MixerContext, SoundDeviceInstance->Handle, KSSTATE_ACQUIRE);
|
|
MMixerSetMidiStatus(&MixerContext, SoundDeviceInstance->Handle, KSSTATE_PAUSE);
|
|
MMixerSetMidiStatus(&MixerContext, SoundDeviceInstance->Handle, KSSTATE_RUN);
|
|
}
|
|
else
|
|
{
|
|
MMixerSetMidiStatus(&MixerContext, SoundDeviceInstance->Handle, KSSTATE_PAUSE);
|
|
MMixerSetMidiStatus(&MixerContext, SoundDeviceInstance->Handle, KSSTATE_ACQUIRE);
|
|
MMixerSetMidiStatus(&MixerContext, SoundDeviceInstance->Handle, KSSTATE_STOP);
|
|
}
|
|
}
|
|
|
|
return MMSYSERR_NOERROR;
|
|
}
|
|
|
|
MMRESULT
|
|
WdmAudResetStreamByMMixer(
|
|
IN struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance,
|
|
IN MMDEVICE_TYPE DeviceType,
|
|
IN BOOLEAN bStartReset)
|
|
{
|
|
MIXER_STATUS Status;
|
|
|
|
if (DeviceType == WAVE_IN_DEVICE_TYPE || DeviceType == WAVE_OUT_DEVICE_TYPE)
|
|
{
|
|
Status = MMixerSetWaveResetState(&MixerContext, SoundDeviceInstance->Handle, bStartReset);
|
|
if (Status == MM_STATUS_SUCCESS)
|
|
{
|
|
/* completed successfully */
|
|
return MMSYSERR_NOERROR;
|
|
}
|
|
}
|
|
|
|
|
|
return MMSYSERR_NOTSUPPORTED;
|
|
}
|
|
|
|
MMRESULT
|
|
WdmAudGetWavePositionByMMixer(
|
|
IN struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance,
|
|
IN MMTIME* Time)
|
|
{
|
|
/* FIXME */
|
|
return MMSYSERR_NOTSUPPORTED;
|
|
}
|
|
|
|
static
|
|
VOID WINAPI
|
|
CommitWaveBufferApc(PVOID ApcContext,
|
|
PIO_STATUS_BLOCK IoStatusBlock,
|
|
ULONG Reserved)
|
|
{
|
|
DWORD dwErrorCode;
|
|
PSOUND_OVERLAPPED Overlap;
|
|
KSSTREAM_HEADER* lpHeader;
|
|
|
|
dwErrorCode = RtlNtStatusToDosError(IoStatusBlock->Status);
|
|
Overlap = (PSOUND_OVERLAPPED)IoStatusBlock;
|
|
lpHeader = Overlap->CompletionContext;
|
|
|
|
/* Call mmebuddy overlap routine */
|
|
Overlap->OriginalCompletionRoutine(dwErrorCode,
|
|
lpHeader->DataUsed, &Overlap->Standard);
|
|
|
|
HeapFree(GetProcessHeap(), 0, lpHeader);
|
|
}
|
|
|
|
MMRESULT
|
|
WdmAudCommitWaveBufferByMMixer(
|
|
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
|
|
IN PVOID OffsetPtr,
|
|
IN DWORD Length,
|
|
IN PSOUND_OVERLAPPED Overlap,
|
|
IN LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine)
|
|
{
|
|
PSOUND_DEVICE SoundDevice;
|
|
MMDEVICE_TYPE DeviceType;
|
|
MMRESULT Result;
|
|
ULONG IoCtl;
|
|
KSSTREAM_HEADER* lpHeader;
|
|
NTSTATUS Status;
|
|
|
|
Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
|
|
|
|
if ( ! MMSUCCESS(Result) )
|
|
{
|
|
return TranslateInternalMmResult(Result);
|
|
}
|
|
|
|
Result = GetSoundDeviceType(SoundDevice, &DeviceType);
|
|
SND_ASSERT( Result == MMSYSERR_NOERROR );
|
|
|
|
lpHeader = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(KSSTREAM_HEADER));
|
|
if ( ! lpHeader )
|
|
{
|
|
/* no memory */
|
|
return MMSYSERR_NOMEM;
|
|
}
|
|
|
|
/* setup stream packet */
|
|
lpHeader->Size = sizeof(KSSTREAM_HEADER);
|
|
lpHeader->PresentationTime.Numerator = 1;
|
|
lpHeader->PresentationTime.Denominator = 1;
|
|
lpHeader->Data = OffsetPtr;
|
|
lpHeader->FrameExtent = Length;
|
|
Overlap->CompletionContext = lpHeader;
|
|
Overlap->OriginalCompletionRoutine = CompletionRoutine;
|
|
IoCtl = (DeviceType == WAVE_OUT_DEVICE_TYPE ? IOCTL_KS_WRITE_STREAM : IOCTL_KS_READ_STREAM);
|
|
|
|
if (DeviceType == WAVE_OUT_DEVICE_TYPE)
|
|
{
|
|
lpHeader->DataUsed = Length;
|
|
}
|
|
|
|
Status = NtDeviceIoControlFile(SoundDeviceInstance->Handle,
|
|
NULL,
|
|
CommitWaveBufferApc,
|
|
NULL,
|
|
(PIO_STATUS_BLOCK)Overlap,
|
|
IoCtl,
|
|
NULL,
|
|
0,
|
|
lpHeader,
|
|
sizeof(KSSTREAM_HEADER));
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* error */
|
|
return MMSYSERR_ERROR;
|
|
}
|
|
|
|
return MMSYSERR_NOERROR;
|
|
}
|