From 87dcb169be3f646d2d1cfa6de35a813f62b6af31 Mon Sep 17 00:00:00 2001 From: Andrew Greenwood Date: Wed, 18 Feb 2009 19:10:02 +0000 Subject: [PATCH] Removed hooks for prepare/unprepare/stream of WAVEHDR. These are handled internally by MME-Buddy. Instead, it simply provides a hook to do the actual streaming, complete with an OVERLAPPED structure and I/O completion routine. Also started imlpementation of wdmaud.drv to begin interaction with code janderwald is working on. svn path=/trunk/; revision=39667 --- reactos/dll/win32/sndblst/sndblst.c | 5 +- reactos/dll/win32/wdmaud.drv/wdmaud.c | 340 +++++++----------- reactos/dll/win32/wdmaud.drv/wdmaud.rbuild | 10 +- reactos/include/reactos/libs/sound/mmebuddy.h | 133 ++++++- .../drivers/sound/mmebuddy/deviceinstance.c | 8 + .../drivers/sound/mmebuddy/mmebuddy.rbuild | 3 +- .../lib/drivers/sound/mmebuddy/wave/header.c | 212 +++++++++-- .../drivers/sound/mmebuddy/wave/streaming.c | 229 ++++++++++++ .../drivers/sound/mmebuddy/wave/wodMessage.c | 2 +- reactos/lib/drivers/sound/mment4/control.c | 2 + 10 files changed, 684 insertions(+), 260 deletions(-) create mode 100644 reactos/lib/drivers/sound/mmebuddy/wave/streaming.c diff --git a/reactos/dll/win32/sndblst/sndblst.c b/reactos/dll/win32/sndblst/sndblst.c index 28cc903d808..076e0caad7a 100644 --- a/reactos/dll/win32/sndblst/sndblst.c +++ b/reactos/dll/win32/sndblst/sndblst.c @@ -98,9 +98,8 @@ BOOLEAN FoundDevice( FuncTable.SetWaveFormat = SetNt4WaveDeviceFormat; FuncTable.Open = OpenNt4SoundDevice; FuncTable.Close = CloseNt4SoundDevice; - FuncTable.PrepareWaveHeader = NULL; - FuncTable.UnprepareWaveHeader = NULL; - FuncTable.SubmitWaveHeader = NULL; + FuncTable.CommitWaveBuffer = WriteFileEx_Committer; + //FuncTable.SubmitWaveHeaderToDevice = SubmitWaveHeaderToDevice; SetSoundDeviceFunctionTable(SoundDevice, &FuncTable); diff --git a/reactos/dll/win32/wdmaud.drv/wdmaud.c b/reactos/dll/win32/wdmaud.drv/wdmaud.c index 2a3d61d426b..2be4f192719 100644 --- a/reactos/dll/win32/wdmaud.drv/wdmaud.c +++ b/reactos/dll/win32/wdmaud.drv/wdmaud.c @@ -1,243 +1,145 @@ /* + * PROJECT: ReactOS Sound System + * LICENSE: GPL - See COPYING in the top level directory + * FILE: dll/win32/wdmaud.drv/wdmaud.c * - * PROJECT: ReactOS WDM Audio driver mapper - * FILE: dll/win32/wdmaud.drv/wdmaud.c - * PURPOSE: wdmaud.drv - * PROGRAMMER: Dmitry Chapyshev (dmitry@reactos.org) + * PURPOSE: WDM Audio Driver (User-mode part) + * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org) + * + * NOTES: Looking for wodMessage & co? You won't find them here. Try + * the MME Buddy library, which is where these routines are + * actually implemented. * - * UPDATE HISTORY: - * 25/05/2008 Created */ -#include - #include -#include +#include +#include #include -#include -#include - -DWORD APIENTRY -mxdMessage(UINT uDevice, - UINT uMsg, - DWORD dwUser, - DWORD dwParam1, - DWORD dwParam2) -{ - DPRINT1("mxdMessage(%04X, %04X, %08X, %08X, %08X);\n", uDevice, uMsg, dwUser, dwParam1, dwParam2); - - switch (uMsg) - { - case MXDM_INIT: - break; - - case MXDM_GETNUMDEVS: - break; - - case MXDM_GETDEVCAPS: - break; - - case MXDM_OPEN: - break; - - case MXDM_CLOSE: - break; - - case MXDM_GETLINEINFO: - break; - - case MXDM_GETLINECONTROLS: - break; - - case MXDM_GETCONTROLDETAILS: - break; - - case MXDM_SETCONTROLDETAILS: - break; - } - - return MMSYSERR_NOTSUPPORTED; -} - -DWORD APIENTRY -auxMessage(UINT uDevice, - UINT uMsg, - DWORD dwUser, - DWORD dwParam1, - DWORD dwParam2) -{ - DPRINT1("auxMessage(%04X, %04X, %08X, %08X, %08X);\n", uDevice, uMsg, dwUser, dwParam1, dwParam2); - - switch (uMsg) - { - case AUXDM_GETDEVCAPS: - - break; - - case AUXDM_GETNUMDEVS: - - break; - - case AUXDM_GETVOLUME: - - break; - - case AUXDM_SETVOLUME: - - break; - - default: - return MMSYSERR_NOTSUPPORTED; - } - - return MMSYSERR_NOTSUPPORTED; -} - -DWORD APIENTRY -wodMessage(UINT uDevice, - UINT uMsg, - DWORD dwUser, - DWORD dwParam1, - DWORD dwParam2) -{ - DPRINT1("wodMessage(%04X, %04X, %08X, %08X, %08X);\n", uDevice, uMsg, dwUser, dwParam1, dwParam2); - - switch (uMsg) - { - case WODM_GETNUMDEVS: - break; - - case WODM_GETDEVCAPS: - break; +#include - case WODM_OPEN: - break; +#define KERNEL_DEVICE_NAME L"\\\\Device\\wdmaud" - case WODM_CLOSE: - break; +HANDLE KernelHandle = INVALID_HANDLE_VALUE; - case WODM_WRITE: - break; - - case WODM_PAUSE: - break; - - case WODM_RESTART: - break; - - case WODM_RESET: - break; - - case WODM_BREAKLOOP: - break; - - case WODM_GETPOS: - break; - - case WODM_SETPITCH: - break; - - case WODM_SETVOLUME: - break; - - case WODM_SETPLAYBACKRATE: - break; - - case WODM_GETPITCH: - break; - - case WODM_GETVOLUME: - break; - - case WODM_GETPLAYBACKRATE: - break; - - default: - return MMSYSERR_NOTSUPPORTED; - } - - return MMSYSERR_NOTSUPPORTED; -} - -DWORD APIENTRY -widMessage(UINT uDevice, - UINT uMsg, - DWORD dwUser, - DWORD dwParam1, - DWORD dwParam2) +APIENTRY LONG +DriverProc( + DWORD DriverId, + HANDLE DriverHandle, + UINT Message, + LONG Parameter1, + LONG Parameter2) { - DPRINT1("widMessage(%04X, %04X, %08X, %08X, %08X);\n", uDevice, uMsg, dwUser, dwParam1, dwParam2); + MMRESULT Result; - switch (uMsg) + switch ( Message ) { - case WIDM_GETNUMDEVS: - break; - - case WIDM_GETDEVCAPS: - break; - - case WIDM_OPEN: - break; - - case WIDM_CLOSE: - break; - - case WIDM_ADDBUFFER: - break; - - case WIDM_STOP: - break; - - case WIDM_START: - break; - - case WIDM_RESET: - break; - - case WIDM_GETPOS: - break; - - default: - return MMSYSERR_NOTSUPPORTED; + case DRV_LOAD : + { + SND_TRACE(L"DRV_LOAD\n"); + + Result = InitEntrypointMutexes(); + + if ( ! MMSUCCESS(Result) ) + return 0L; + + KernelHandle = CreateFile(KERNEL_DEVICE_NAME, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_WRITE, // ok? + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL); + + if ( KernelHandle == INVALID_HANDLE_VALUE ) + { + SND_ERR(L"Failed to open %s", KERNEL_DEVICE_NAME); + CleanupEntrypointMutexes(); + + UnlistAllSoundDevices(); + + return 0L; + } + + SND_TRACE(L"Initialisation complete\n"); + + return 1L; + } + + case DRV_FREE : + { + SND_TRACE(L"DRV_FREE\n"); + + if ( KernelHandle != INVALID_HANDLE_VALUE ) + { + CloseHandle(KernelHandle); + KernelHandle = INVALID_HANDLE_VALUE; + } + + /* TODO: Clean up the path names! */ + UnlistAllSoundDevices(); + + CleanupEntrypointMutexes(); + + SND_TRACE(L"Unfreed memory blocks: %d\n", + GetMemoryAllocationCount()); + + return 1L; + } + + case DRV_ENABLE : + case DRV_DISABLE : + { + SND_TRACE(L"DRV_ENABLE / DRV_DISABLE\n"); + return 1L; + } + + case DRV_OPEN : + case DRV_CLOSE : + { + SND_TRACE(L"DRV_OPEN / DRV_CLOSE\n"); + return 1L; + } + + case DRV_QUERYCONFIGURE : + { + SND_TRACE(L"DRV_QUERYCONFIGURE"); + return 0L; + } + case DRV_CONFIGURE : + return DRVCNF_OK; + + default : + SND_TRACE(L"Unhandled message %d\n", Message); + return DefDriverProc(DriverId, + DriverHandle, + Message, + Parameter1, + Parameter2); } - - return MMSYSERR_NOTSUPPORTED; -} - -DWORD APIENTRY -modMessage(UINT uDevice, - UINT uMsg, - DWORD dwUser, - DWORD dwParam1, - DWORD dwParam2) -{ - DPRINT1("modMessage(%04X, %04X, %08X, %08X, %08X);\n", uDevice, uMsg, dwUser, dwParam1, dwParam2); - - return MMSYSERR_NOTSUPPORTED; } -LRESULT APIENTRY -DriverProc(DWORD dwDriverID, - HDRVR hDriver, - UINT uiMessage, - LPARAM lParam1, - LPARAM lParam2) -{ - return DefDriverProc(dwDriverID, hDriver, uiMessage, lParam1, lParam2); -} -BOOL WINAPI -DllMain(IN HINSTANCE hinstDLL, - IN DWORD dwReason, - IN LPVOID lpvReserved) +BOOL WINAPI DllMain( + HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved) { - switch (dwReason) + switch ( fdwReason ) { - case DLL_PROCESS_ATTACH: - DisableThreadLibraryCalls(hinstDLL); + case DLL_PROCESS_ATTACH : + SND_TRACE(L"WDMAUD.DRV - Process attached\n"); + break; + case DLL_PROCESS_DETACH : + SND_TRACE(L"WDMAUD.DRV - Process detached\n"); + break; + case DLL_THREAD_ATTACH : + SND_TRACE(L"WDMAUD.DRV - Thread attached\n"); + break; + case DLL_THREAD_DETACH : + SND_TRACE(L"WDMAUD.DRV - Thread detached\n"); break; } return TRUE; } - diff --git a/reactos/dll/win32/wdmaud.drv/wdmaud.rbuild b/reactos/dll/win32/wdmaud.drv/wdmaud.rbuild index b2a064081ac..7237b5917d2 100644 --- a/reactos/dll/win32/wdmaud.drv/wdmaud.rbuild +++ b/reactos/dll/win32/wdmaud.drv/wdmaud.rbuild @@ -1,12 +1,14 @@ - + . - - advapi32 + include/reactos/libs/sound + + mmebuddy + ntdll kernel32 - winmm user32 winmm + advapi32 wdmaud.c wdmaud.rc diff --git a/reactos/include/reactos/libs/sound/mmebuddy.h b/reactos/include/reactos/libs/sound/mmebuddy.h index c4b2291c4d8..2cf40fb96de 100644 --- a/reactos/include/reactos/libs/sound/mmebuddy.h +++ b/reactos/include/reactos/libs/sound/mmebuddy.h @@ -32,7 +32,7 @@ MessageBox(0, dbg_popup_msg, dbg_popup_title, MB_OK | MB_TASKMODAL); \ } -#ifdef DEBUG_NT4 +#ifndef NDEBUG #define SND_ERR(...) \ { \ WCHAR dbg_popup_msg[1024]; \ @@ -62,11 +62,24 @@ } \ } + #define DUMP_WAVEHDR_QUEUE(sound_device_instance) \ + { \ + PWAVEHDR CurrDumpHdr = sound_device_instance->HeadWaveHeader; \ + SND_TRACE(L"-- Current wave header list --\n"); \ + while ( CurrDumpHdr ) \ + { \ + SND_TRACE(L"%x | %d bytes | flags: %x\n", CurrDumpHdr->lpData, \ + CurrDumpHdr->dwBufferLength, \ + CurrDumpHdr->dwFlags); \ + CurrDumpHdr = CurrDumpHdr->lpNext; \ + } \ + } + #else - #define SND_ERR(...) while ( 0 ) do {} - #define SND_WARN(...) while ( 0 ) do {} - #define SND_TRACE(...) while ( 0 ) do {} - #define SND_ASSERT(condition) while ( 0 ) do {} + #define SND_ERR(...) do {} while ( 0 ) + #define SND_WARN(...) do {} while ( 0 ) + #define SND_TRACE(...) do {} while ( 0 ) + #define SND_ASSERT(condition) do {} while ( 0 ) #endif /* @@ -157,6 +170,28 @@ DEFINE_GETCAPS_FUNCTYPE(MMGETMIDIINCAPS_FUNC, LPMIDIINCAPS ); struct _SOUND_DEVICE; struct _SOUND_DEVICE_INSTANCE; + +/* + By extending the OVERLAPPED structure, it becomes possible to provide the + I/O completion routines with additional information. +*/ + +typedef struct _SOUND_OVERLAPPED +{ + OVERLAPPED Standard; + struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance; + PWAVEHDR Header; +} SOUND_OVERLAPPED, *PSOUND_OVERLAPPED; + +typedef MMRESULT (*WAVE_COMMIT_FUNC)( + IN struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance, + IN PVOID OffsetPtr, + IN DWORD Bytes, + IN PSOUND_OVERLAPPED Overlap, + IN LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine); + + + typedef MMRESULT (*MMWAVEQUERYFORMATSUPPORT_FUNC)( IN struct _SOUND_DEVICE* Device, IN PWAVEFORMATEX WaveFormat, @@ -179,6 +214,11 @@ typedef MMRESULT (*MMWAVEHEADER_FUNC)( IN struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance, IN PWAVEHDR WaveHeader); +typedef MMRESULT (*MMBUFFER_FUNC)( + IN struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance, + IN PVOID Buffer, + IN DWORD Length); + typedef struct _MMFUNCTION_TABLE { union @@ -196,11 +236,19 @@ typedef struct _MMFUNCTION_TABLE MMWAVEQUERYFORMATSUPPORT_FUNC QueryWaveFormatSupport; MMWAVESETFORMAT_FUNC SetWaveFormat; - MMWAVEHEADER_FUNC PrepareWaveHeader; - MMWAVEHEADER_FUNC UnprepareWaveHeader; - MMWAVEHEADER_FUNC SubmitWaveHeader; + WAVE_COMMIT_FUNC CommitWaveBuffer; + + // Redundant + //MMWAVEHEADER_FUNC PrepareWaveHeader; + //MMWAVEHEADER_FUNC UnprepareWaveHeader; + //MMWAVEHEADER_FUNC WriteWaveHeader; + + //MMWAVEHEADER_FUNC SubmitWaveHeaderToDevice; + //MMBUFFER_FUNC CompleteBuffer; } MMFUNCTION_TABLE, *PMMFUNCTION_TABLE; + + typedef MMRESULT (*SOUND_THREAD_REQUEST_HANDLER)( IN struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance, IN PVOID Parameter); @@ -252,8 +300,32 @@ typedef struct _SOUND_DEVICE_INSTANCE DWORD ClientCallback; DWORD ClientCallbackInstanceData; } WinMM; + + /* DO NOT TOUCH THESE OUTSIDE OF THE SOUND THREAD */ + + union + { + PWAVEHDR HeadWaveHeader; + }; + + union + { + PWAVEHDR TailWaveHeader; + }; + + PWAVEHDR WaveLoopStart; + PWAVEHDR CurrentWaveHeader; + DWORD OutstandingBuffers; } SOUND_DEVICE_INSTANCE, *PSOUND_DEVICE_INSTANCE; +/* This lives in WAVEHDR.reserved */ +typedef struct _WAVEHDR_EXTENSION +{ + DWORD BytesCommitted; + DWORD BytesCompleted; +} WAVEHDR_EXTENSION, *PWAVEHDR_EXTENSION; + + /* reentrancy.c */ @@ -308,8 +380,8 @@ MmeCloseDevice( #define MmeUnprepareWaveHeader(private_handle, header) \ UnprepareWaveHeader((PSOUND_DEVICE_INSTANCE)private_handle, (PWAVEHDR)header) -#define MmeEnqueueWaveHeader(private_handle, header) \ - EnqueueWaveHeader((PSOUND_DEVICE_INSTANCE)private_handle, (PWAVEHDR)header) +#define MmeWriteWaveHeader(private_handle, header) \ + WriteWaveHeader((PSOUND_DEVICE_INSTANCE)private_handle, (PWAVEHDR)header) /* @@ -493,6 +565,16 @@ SetWaveDeviceFormat( wave/header.c */ +MMRESULT +EnqueueWaveHeader( + PSOUND_DEVICE_INSTANCE SoundDeviceInstance, + IN PVOID Parameter); + +VOID +CompleteWaveHeader( + IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, + IN PWAVEHDR Header); + MMRESULT PrepareWaveHeader( IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, @@ -504,11 +586,40 @@ UnprepareWaveHeader( IN PWAVEHDR Header); MMRESULT -EnqueueWaveHeader( +WriteWaveHeader( IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, IN PWAVEHDR Header); +/* + wave/streaming.c +*/ + +MMRESULT +DoWaveStreaming( + IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance); + +VOID CALLBACK +CompleteIO( + IN DWORD dwErrorCode, + IN DWORD dwNumberOfBytesTransferred, + IN LPOVERLAPPED lpOverlapped); + +MMRESULT +CommitWaveHeaderToKernelDevice( + IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, + IN PWAVEHDR Header, + IN WAVE_COMMIT_FUNC CommitFunction); + +MMRESULT +WriteFileEx_Committer( + IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, + IN PVOID OffsetPtr, + IN DWORD Length, + IN PSOUND_OVERLAPPED Overlap, + IN LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine); + + /* kernel.c */ diff --git a/reactos/lib/drivers/sound/mmebuddy/deviceinstance.c b/reactos/lib/drivers/sound/mmebuddy/deviceinstance.c index 64dcbff5531..922ed1d649c 100644 --- a/reactos/lib/drivers/sound/mmebuddy/deviceinstance.c +++ b/reactos/lib/drivers/sound/mmebuddy/deviceinstance.c @@ -186,6 +186,14 @@ CreateSoundDeviceInstance( (*SoundDeviceInstance)->WinMM.ClientCallbackInstanceData = 0; (*SoundDeviceInstance)->WinMM.Flags = 0; + /* Initialise the members of the struct used by the sound thread */ + (*SoundDeviceInstance)->HeadWaveHeader = NULL; + (*SoundDeviceInstance)->TailWaveHeader = NULL; + + (*SoundDeviceInstance)->CurrentWaveHeader = NULL; + (*SoundDeviceInstance)->OutstandingBuffers = 0; + // TODO: Loop + /* Create the streaming thread (TODO - is this for wave only?) */ Result = CreateSoundThread(&(*SoundDeviceInstance)->Thread); if ( ! MMSUCCESS(Result) ) diff --git a/reactos/lib/drivers/sound/mmebuddy/mmebuddy.rbuild b/reactos/lib/drivers/sound/mmebuddy/mmebuddy.rbuild index fe19ac58bc1..b228b845587 100644 --- a/reactos/lib/drivers/sound/mmebuddy/mmebuddy.rbuild +++ b/reactos/lib/drivers/sound/mmebuddy/mmebuddy.rbuild @@ -16,7 +16,8 @@ widMessage.c wodMessage.c format.c - header.c + header.c + streaming.c midMessage.c diff --git a/reactos/lib/drivers/sound/mmebuddy/wave/header.c b/reactos/lib/drivers/sound/mmebuddy/wave/header.c index fbb0060cdc6..0cfb5537d2f 100644 --- a/reactos/lib/drivers/sound/mmebuddy/wave/header.c +++ b/reactos/lib/drivers/sound/mmebuddy/wave/header.c @@ -1,9 +1,9 @@ /* * PROJECT: ReactOS Sound System "MME Buddy" Library * LICENSE: GPL - See COPYING in the top level directory - * FILE: lib/drivers/sound/mmebuddy/header.c + * FILE: lib/drivers/sound/mmebuddy/wave/header.c * - * PURPOSE: Wave header preparation routines + * PURPOSE: Wave header preparation and submission routines * * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org) */ @@ -13,6 +13,7 @@ #include #include #include +#include /* @@ -59,6 +60,25 @@ WaveHeaderOperation( } +/* + SanitizeWaveHeader + Clean up a header / reinitialize +*/ + +VOID +SanitizeWaveHeader( + PWAVEHDR Header) +{ + PWAVEHDR_EXTENSION Extension = (PWAVEHDR_EXTENSION) Header->reserved; + SND_ASSERT( Extension ); + + Header->dwBytesRecorded = 0; + + Extension->BytesCommitted = 0; + Extension->BytesCompleted = 0; +} + + /* The following routines are basically handlers for: - WODM_PREPARE @@ -78,6 +98,7 @@ PrepareWaveHeader( MMRESULT Result; PSOUND_DEVICE SoundDevice; PMMFUNCTION_TABLE FunctionTable; + PWAVEHDR_EXTENSION Extension; VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) ); VALIDATE_MMSYS_PARAMETER( Header ); @@ -92,12 +113,18 @@ PrepareWaveHeader( if ( ! MMSUCCESS(Result) ) return TranslateInternalMmResult(Result); - if ( ! FunctionTable->PrepareWaveHeader ) - return MMSYSERR_NOTSUPPORTED; + Extension = AllocateStruct(WAVEHDR_EXTENSION); + if ( ! Extension ) + return MMSYSERR_NOMEM; + + Header->reserved = (DWORD_PTR) Extension; + Extension->BytesCommitted = 0; + Extension->BytesCompleted = 0; + + /* Configure the flags */ + Header->dwFlags |= WHDR_PREPARED; - return WaveHeaderOperation(FunctionTable->PrepareWaveHeader, - SoundDeviceInstance, - Header); + return MMSYSERR_NOERROR; } MMRESULT @@ -108,6 +135,7 @@ UnprepareWaveHeader( MMRESULT Result; PSOUND_DEVICE SoundDevice; PMMFUNCTION_TABLE FunctionTable; + PWAVEHDR_EXTENSION Extension; VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) ); VALIDATE_MMSYS_PARAMETER( Header ); @@ -122,16 +150,18 @@ UnprepareWaveHeader( if ( ! MMSUCCESS(Result) ) return TranslateInternalMmResult(Result); - if ( ! FunctionTable->UnprepareWaveHeader ) - return MMSYSERR_NOTSUPPORTED; + SND_ASSERT( Header->reserved ); + Extension = (PWAVEHDR_EXTENSION) Header->reserved; + FreeMemory(Extension); - return WaveHeaderOperation(FunctionTable->UnprepareWaveHeader, - SoundDeviceInstance, - Header); + /* Configure the flags */ + Header->dwFlags &= ~WHDR_PREPARED; + + return MMSYSERR_NOERROR; } MMRESULT -EnqueueWaveHeader( +WriteWaveHeader( IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, IN PWAVEHDR Header) { @@ -152,7 +182,7 @@ EnqueueWaveHeader( if ( ! MMSUCCESS(Result) ) return TranslateInternalMmResult(Result); - if ( ! FunctionTable->SubmitWaveHeader ) + if ( ! FunctionTable->CommitWaveBuffer ) return MMSYSERR_NOTSUPPORTED; /* @@ -164,20 +194,160 @@ EnqueueWaveHeader( VALIDATE_MMSYS_PARAMETER( Header->dwFlags & WHDR_PREPARED ); VALIDATE_MMSYS_PARAMETER( ! (Header->dwFlags & WHDR_INQUEUE) ); + SanitizeWaveHeader(Header); + /* Clear the "done" flag for the buffer */ Header->dwFlags &= ~WHDR_DONE; - Result = WaveHeaderOperation(FunctionTable->SubmitWaveHeader, - SoundDeviceInstance, - Header); + Result = CallSoundThread(SoundDeviceInstance, + EnqueueWaveHeader, + Header); - if ( ! MMSUCCESS(Result) ) + return Result; +} + + +/* + EnqueueWaveHeader + Put the header in the record/playback queue. This is performed within + the context of the sound thread, it must NEVER be called from another + thread. + + CompleteWaveHeader + Set the header information to indicate that it has finished playing, + and return it to the client application. This again must be called + within the context of the sound thread. +*/ + +MMRESULT +EnqueueWaveHeader( + PSOUND_DEVICE_INSTANCE SoundDeviceInstance, + IN PVOID Parameter) +{ + VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance ); + VALIDATE_MMSYS_PARAMETER( Parameter ); + + PWAVEHDR WaveHeader = (PWAVEHDR) Parameter; + + /* Initialise */ + WaveHeader->lpNext = NULL; + + /* Set the "in queue" flag */ + WaveHeader->dwFlags |= WHDR_INQUEUE; + + if ( ! SoundDeviceInstance->TailWaveHeader ) + { + /* This is the first header in the queue */ + SND_TRACE(L"Enqueued first wave header\n"); + SoundDeviceInstance->HeadWaveHeader = WaveHeader; + SoundDeviceInstance->TailWaveHeader = WaveHeader; + + DoWaveStreaming(SoundDeviceInstance); + } + else { - return Result; + /* There are already queued headers - make this one the tail */ + SND_TRACE(L"Enqueued next wave header\n"); + SoundDeviceInstance->TailWaveHeader->lpNext = WaveHeader; + SoundDeviceInstance->TailWaveHeader = WaveHeader; + + DoWaveStreaming(SoundDeviceInstance); } - /* Set the "in queue" flag if everything was OK */ - Header->dwFlags |= WHDR_INQUEUE; + DUMP_WAVEHDR_QUEUE(SoundDeviceInstance); return MMSYSERR_NOERROR; } + +VOID +CompleteWaveHeader( + IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, + IN PWAVEHDR Header) +{ + PWAVEHDR PrevHdr = NULL, CurrHdr = NULL; + PWAVEHDR_EXTENSION Extension; + PSOUND_DEVICE SoundDevice; + MMDEVICE_TYPE DeviceType; + MMRESULT Result; + + SND_TRACE(L"BUFFER COMPLETE :)\n"); + + // TODO: Set header flags? + // TODO: Call client + // TODO: Streaming + + //DoWaveStreaming(SoundDeviceInstance); + + Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice); + SND_ASSERT( MMSUCCESS(Result) ); + Result = GetSoundDeviceType(SoundDevice, &DeviceType); + SND_ASSERT( MMSUCCESS(Result) ); + + Extension = (PWAVEHDR_EXTENSION)Header->reserved; + SND_ASSERT( Extension ); + + /* Remove the header from the queue, like so */ + if ( SoundDeviceInstance->HeadWaveHeader == Header ) + { + SoundDeviceInstance->HeadWaveHeader = Header->lpNext; + + SND_TRACE(L"Dropping head node\n"); + + /* If nothing after the head, then there is no tail */ + if ( Header->lpNext == NULL ) + { + SND_TRACE(L"Dropping tail node\n"); + SoundDeviceInstance->TailWaveHeader = NULL; + } + } + else + { + PrevHdr = NULL; + CurrHdr = SoundDeviceInstance->HeadWaveHeader; + + SND_TRACE(L"Relinking nodes\n"); + + while ( CurrHdr != Header ) + { + PrevHdr = CurrHdr; + CurrHdr = CurrHdr->lpNext; + SND_ASSERT( CurrHdr ); + } + + SND_ASSERT( PrevHdr ); + + PrevHdr->lpNext = CurrHdr->lpNext; + + /* If this is the tail node, update the tail */ + if ( Header->lpNext == NULL ) + { + SND_TRACE(L"Updating tail node\n"); + SoundDeviceInstance->TailWaveHeader = PrevHdr; + } + } + + /* Make sure we're not using this as the current buffer any more, either! */ + if ( SoundDeviceInstance->CurrentWaveHeader == Header ) + { + SoundDeviceInstance->CurrentWaveHeader = Header->lpNext; + } + + DUMP_WAVEHDR_QUEUE(SoundDeviceInstance); + + SND_TRACE(L"Returning buffer to client...\n"); + + /* Update the header */ + Header->dwFlags &= ~WHDR_INQUEUE; + Header->dwFlags |= WHDR_DONE; + + if ( DeviceType == WAVE_IN_DEVICE_TYPE ) + { + // FIXME: We won't be called on incomplete buffer! + Header->dwBytesRecorded = Extension->BytesCompleted; + } + + /* Safe to do this without thread protection, as we're done with the header */ + NotifyMmeClient(SoundDeviceInstance, + DeviceType == WAVE_OUT_DEVICE_TYPE ? WOM_DONE : WIM_DATA, + (DWORD) Header); +} diff --git a/reactos/lib/drivers/sound/mmebuddy/wave/streaming.c b/reactos/lib/drivers/sound/mmebuddy/wave/streaming.c new file mode 100644 index 00000000000..5fdf4958fa0 --- /dev/null +++ b/reactos/lib/drivers/sound/mmebuddy/wave/streaming.c @@ -0,0 +1,229 @@ +/* + * PROJECT: ReactOS Sound System "MME Buddy" Library + * LICENSE: GPL - See COPYING in the top level directory + * FILE: lib/drivers/sound/mmebuddy/wave/streaming.c + * + * PURPOSE: Wave streaming + * + * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org) +*/ + +#include +#include +#include +#include +#include +#include + + +/* + Restrain ourselves from flooding the kernel device! +*/ + +#define SOUND_KERNEL_BUFFER_COUNT 10 +#define SOUND_KERNEL_BUFFER_SIZE 200000 + + +/* + DoWaveStreaming + Check if there is streaming to be done, and if so, do it. +*/ + +MMRESULT +DoWaveStreaming( + IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance) +{ + MMRESULT Result; + PSOUND_DEVICE SoundDevice; + PMMFUNCTION_TABLE FunctionTable; + + Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice); + SND_ASSERT( MMSUCCESS(Result) ); + + Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable); + SND_ASSERT( MMSUCCESS(Result) ); + SND_ASSERT( FunctionTable ); + SND_ASSERT( FunctionTable->CommitWaveBuffer ); + + // HACK + SND_TRACE(L"Calling buffer submit routine\n"); + + if ( ! SoundDeviceInstance->CurrentWaveHeader ) + { + /* Start from the beginning (always a good idea) */ + SoundDeviceInstance->CurrentWaveHeader = SoundDeviceInstance->HeadWaveHeader; + } + + if ( SoundDeviceInstance->CurrentWaveHeader ) + { + /* Stream or continue streaming this header */ + + Result = CommitWaveHeaderToKernelDevice(SoundDeviceInstance, + SoundDeviceInstance->CurrentWaveHeader, + FunctionTable->CommitWaveBuffer); + } + else + { + SND_TRACE(L"NOTHING TO DO - REC/PLAY STOPPED\n"); + } + + return Result; +} + + +/* + CompleteIO + An APC called as a result of a call to CommitWaveHeaderToKernelDevice. + This will count up the number of bytes which have been dealt with, + and when the entire wave header has been dealt with, will call + CompleteWaveHeader to have the wave header returned to the client. + + CommitWaveHeaderToKernelDevice + Sends portions of the buffer described by the wave header to a kernel + device. This must only be called from within the context of the sound + thread. The caller supplies either their own commit routine, or uses + WriteFileEx_Committer. The committer is called with portions of the + buffer specified in the wave header. + + WriteFileEx_Committer + Commit buffers using the WriteFileEx API. +*/ + +VOID CALLBACK +CompleteIO( + IN DWORD dwErrorCode, + IN DWORD dwNumberOfBytesTransferred, + IN LPOVERLAPPED lpOverlapped) +{ + PSOUND_DEVICE_INSTANCE SoundDeviceInstance; + PSOUND_OVERLAPPED SoundOverlapped = (PSOUND_OVERLAPPED) lpOverlapped; + PWAVEHDR WaveHdr; + PWAVEHDR_EXTENSION HdrExtension; + + WaveHdr = (PWAVEHDR) SoundOverlapped->Header; + SND_ASSERT( WaveHdr ); + + HdrExtension = (PWAVEHDR_EXTENSION) WaveHdr->reserved; + SND_ASSERT( HdrExtension ); + + SoundDeviceInstance = SoundOverlapped->SoundDeviceInstance; + + HdrExtension->BytesCompleted += dwNumberOfBytesTransferred; + SND_TRACE(L"%d/%d bytes of wavehdr completed\n", HdrExtension->BytesCompleted, WaveHdr->dwBufferLength); + + /* We have an available buffer now */ + -- SoundDeviceInstance->OutstandingBuffers; + + if ( HdrExtension->BytesCompleted == WaveHdr->dwBufferLength ) + { + CompleteWaveHeader(SoundDeviceInstance, WaveHdr); + } + + DoWaveStreaming(SoundDeviceInstance); + + //CompleteWavePortion(SoundDeviceInstance, dwNumberOfBytesTransferred); + + FreeMemory(lpOverlapped); +} + +MMRESULT +CommitWaveHeaderToKernelDevice( + IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, + IN PWAVEHDR Header, + IN WAVE_COMMIT_FUNC CommitFunction) +{ + PSOUND_OVERLAPPED Overlap; + DWORD BytesToWrite, BytesRemaining; + PWAVEHDR_EXTENSION HdrExtension; + LPVOID Offset; + + VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance ); + VALIDATE_MMSYS_PARAMETER( Header ); + VALIDATE_MMSYS_PARAMETER( CommitFunction ); + + HdrExtension = (PWAVEHDR_EXTENSION) Header->reserved; + VALIDATE_MMSYS_PARAMETER( HdrExtension ); + + /* Loop whilst there is data and sufficient available buffers */ + while ( ( SoundDeviceInstance->OutstandingBuffers < SOUND_KERNEL_BUFFER_COUNT ) && + ( HdrExtension->BytesCommitted < Header->dwBufferLength ) ) + { + /* Is this the start of a loop? */ + SoundDeviceInstance->WaveLoopStart = Header; + + /* Where to start pulling the data from within the buffer */ + Offset = Header->lpData + HdrExtension->BytesCommitted; + + /* How much of this header is not committed? */ + BytesRemaining = Header->dwBufferLength - HdrExtension->BytesCommitted; + + /* We can write anything up to the buffer size limit */ + BytesToWrite = BytesRemaining > SOUND_KERNEL_BUFFER_SIZE ? + SOUND_KERNEL_BUFFER_SIZE : + BytesRemaining; + + /* If there's nothing left in the current header, move to the next */ + if ( BytesToWrite == 0 ) + { + Header = Header->lpNext; + HdrExtension = (PWAVEHDR_EXTENSION) Header->reserved; + SND_ASSERT( HdrExtension ); + SND_ASSERT( HdrExtension->BytesCommitted == 0 ); + SND_ASSERT( HdrExtension->BytesCompleted == 0 ); + continue; + } + + HdrExtension->BytesCommitted += BytesToWrite; + + /* We're using up a buffer so update this */ + ++ SoundDeviceInstance->OutstandingBuffers; + + SND_TRACE(L"COMMIT: Offset 0x%x amount %d remain %d totalcommit %d", + Offset, BytesToWrite, BytesRemaining, HdrExtension->BytesCommitted); + + /* We need a new overlapped info structure for each buffer */ + Overlap = AllocateStruct(SOUND_OVERLAPPED); + + if ( Overlap ) + { + ZeroMemory(Overlap, sizeof(SOUND_OVERLAPPED)); + Overlap->SoundDeviceInstance = SoundDeviceInstance; + Overlap->Header = Header; + + + if ( ! MMSUCCESS(CommitFunction(SoundDeviceInstance, Offset, BytesToWrite, Overlap, CompleteIO)) ) + { + /* Just pretend it played if we fail... Show must go on, etc. etc. */ + SND_WARN(L"FAILED\n"); + HdrExtension->BytesCompleted += BytesToWrite; + } + } + } + + return MMSYSERR_NOERROR; +} + +MMRESULT +WriteFileEx_Committer( + IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, + IN PVOID OffsetPtr, + IN DWORD Length, + IN PSOUND_OVERLAPPED Overlap, + IN LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine) +{ + HANDLE Handle; + + VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance ); + VALIDATE_MMSYS_PARAMETER( OffsetPtr ); + VALIDATE_MMSYS_PARAMETER( Overlap ); + VALIDATE_MMSYS_PARAMETER( CompletionRoutine ); + + GetSoundDeviceInstanceHandle(SoundDeviceInstance, &Handle); + + if ( ! WriteFileEx(Handle, OffsetPtr, Length, (LPOVERLAPPED)Overlap, CompletionRoutine) ) + { + // TODO + } + + return MMSYSERR_NOERROR; +} diff --git a/reactos/lib/drivers/sound/mmebuddy/wave/wodMessage.c b/reactos/lib/drivers/sound/mmebuddy/wave/wodMessage.c index 754b8c37324..051f24014d8 100644 --- a/reactos/lib/drivers/sound/mmebuddy/wave/wodMessage.c +++ b/reactos/lib/drivers/sound/mmebuddy/wave/wodMessage.c @@ -94,7 +94,7 @@ wodMessage( case WODM_WRITE : { - Result = MmeEnqueueWaveHeader(PrivateHandle, Parameter1); + Result = MmeWriteWaveHeader(PrivateHandle, Parameter1); break; } diff --git a/reactos/lib/drivers/sound/mment4/control.c b/reactos/lib/drivers/sound/mment4/control.c index 62891876094..0d32d2e09ea 100644 --- a/reactos/lib/drivers/sound/mment4/control.c +++ b/reactos/lib/drivers/sound/mment4/control.c @@ -8,6 +8,8 @@ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org) */ +#define NDEBUG + #include #include #include -- 2.17.1