Author: silverblade Date: Sun Jul 6 18:12:29 2008 New Revision: 34349
URL: http://svn.reactos.org/svn/reactos?rev=34349&view=rev Log: Sound device I/O completion abstraction. thread.c now handles the overlapped I/O structure allocations etc. and gathers completed I/O requests, tagging them with contextual information within the I/O completion APC. Once the APC returns, the completed I/O requests are passed to a callback routine. This allows wavethread.c to just supply "request" and "I/O completion" callbacks. The I/O completion callback's context data is then cast to WAVEHDR...
Modified: branches/silverblade-audio/include/reactos/libs/sound/mmebuddy.h branches/silverblade-audio/lib/drivers/sound/mmebuddy/thread.c branches/silverblade-audio/lib/drivers/sound/mmebuddy/wave/wavethread.c
Modified: branches/silverblade-audio/include/reactos/libs/sound/mmebuddy.h URL: http://svn.reactos.org/svn/reactos/branches/silverblade-audio/include/reacto... ============================================================================== --- branches/silverblade-audio/include/reactos/libs/sound/mmebuddy.h [iso-8859-1] (original) +++ branches/silverblade-audio/include/reactos/libs/sound/mmebuddy.h [iso-8859-1] Sun Jul 6 18:12:29 2008 @@ -101,23 +101,49 @@ MMRESULT Result; } THREAD_REQUEST, *PTHREAD_REQUEST;
+typedef struct _SOUND_THREAD_COMPLETED_IO +{ + struct _SOUND_THREAD_COMPLETED_IO* Next; + PVOID ContextData; /* eg: PWAVEHDR */ + DWORD BytesTransferred; +} SOUND_THREAD_COMPLETED_IO, *PSOUND_THREAD_COMPLETED_IO; + +typedef struct _SOUND_THREAD_OVERLAPPED +{ + OVERLAPPED General; + struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance; + PVOID ContextData; /* eg: PWAVEHDR */ + PSOUND_THREAD_COMPLETED_IO CompletionData; +} SOUND_THREAD_OVERLAPPED, *PSOUND_THREAD_OVERLAPPED; + typedef MMRESULT (*SOUND_THREAD_REQUEST_HANDLER)( IN struct _SOUND_DEVICE_INSTANCE* Instance, IN PVOID PrivateThreadData, IN DWORD RequestId, IN PVOID Data);
+typedef VOID (*SOUND_THREAD_IO_COMPLETION_HANDLER)( + IN struct _SOUND_DEVICE_INSTANCE* Instance, + IN PVOID ContextData, + IN DWORD BytesTransferred); + typedef struct _SOUND_THREAD { /* Thread management */ HANDLE Handle; PVOID PrivateData; BOOLEAN Running; - SOUND_THREAD_REQUEST_HANDLER RequestHandler; + HANDLE ReadyEvent; /* Thread waiting for a request */ HANDLE RequestEvent; /* Caller sending a request */ HANDLE DoneEvent; /* Thread completed a request */ + + SOUND_THREAD_REQUEST_HANDLER RequestHandler; THREAD_REQUEST Request; + + SOUND_THREAD_OVERLAPPED Overlapped; + PSOUND_THREAD_COMPLETED_IO FirstCompletedIo; + SOUND_THREAD_IO_COMPLETION_HANDLER IoCompletionHandler; } SOUND_THREAD, *PSOUND_THREAD;
@@ -142,6 +168,8 @@ PWAVEHDR CurrentBuffer; PWAVEHDR FirstBuffer; PWAVEHDR LastBuffer; + + /*DWORD RemainingBytes;*/ } WAVE_THREAD_DATA, *PWAVE_THREAD_DATA;
/* @@ -476,7 +504,8 @@ StartSoundThread( IN PSOUND_DEVICE_INSTANCE Instance, IN SOUND_THREAD_REQUEST_HANDLER RequestHandler, - IN PVOID Data); + IN SOUND_THREAD_IO_COMPLETION_HANDLER IoCompletionHandler, + IN LPVOID PrivateThreadData);
MMRESULT StopSoundThread( @@ -492,6 +521,19 @@ GetSoundThreadPrivateData( IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, OUT PVOID* PrivateData); + +VOID CALLBACK +CompleteSoundThreadIo( + IN DWORD dwErrorCode, + IN DWORD dwNumberOfBytesTransferred, + IN LPOVERLAPPED lpOverlapped); + +MMRESULT +OverlappedWriteToSoundDevice( + IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, + IN PVOID ContextData, + IN PVOID Buffer, + IN DWORD BufferSize);
Modified: branches/silverblade-audio/lib/drivers/sound/mmebuddy/thread.c URL: http://svn.reactos.org/svn/reactos/branches/silverblade-audio/lib/drivers/so... ============================================================================== --- branches/silverblade-audio/lib/drivers/sound/mmebuddy/thread.c [iso-8859-1] (original) +++ branches/silverblade-audio/lib/drivers/sound/mmebuddy/thread.c [iso-8859-1] Sun Jul 6 18:12:29 2008 @@ -11,6 +11,7 @@ History: 4 July 2008 - Created 5 July 2008 - Implemented basic request processing + 6 July 2008 - Added I/O completion handling */
/* @@ -24,21 +25,24 @@ #include <mmebuddy.h>
+/* + This is the sound thread's processing loop. Its job is to aid in + asynchronous streaming of sound data with a kernel-mode driver. It's + basically a loop in which we wait for a request from the client, or + completed data from the kernel-mode driver. + + When either of these events occur, the relevant details get passed on + to one of the routines specified when the thread was started. +*/ DWORD WINAPI SoundThreadProc( IN LPVOID lpParameter) { PSOUND_DEVICE_INSTANCE Instance; PSOUND_THREAD Thread; - /*HANDLE Events[2];*/
Instance = (PSOUND_DEVICE_INSTANCE) lpParameter; Thread = Instance->Thread; - -/* - Events[0] = Thread->KillEvent; - Events[1] = Thread->RequestEvent; -*/
Thread->Running = TRUE;
@@ -76,7 +80,29 @@ else if ( WaitResult == WAIT_IO_COMPLETION ) { /* This gets called after I/O completion */ - /* Do we need to do anything special here? */ + PSOUND_THREAD_COMPLETED_IO CompletionData; + SOUND_ASSERT(Thread->FirstCompletedIo); + + SOUND_DEBUG(L"Outside I/O completion APC"); + + /* + Purge the completed data queue + FIXME? This will be done in the WRONG ORDER! + Is this such a problem? The caller won't care. We'll need + to remove them from our queue though! + */ + while ( (CompletionData = Thread->FirstCompletedIo) ) + { + /* Call high-level custom I/O completion routine */ + Thread->IoCompletionHandler(Instance, + CompletionData->ContextData, + CompletionData->BytesTransferred); + + /* TODO: I'm sure I've forgotten something ... */ + + Thread->FirstCompletedIo = CompletionData->Next; + FreeMemory(CompletionData); + } } }
@@ -141,6 +167,7 @@ StartSoundThread( IN PSOUND_DEVICE_INSTANCE Instance, IN SOUND_THREAD_REQUEST_HANDLER RequestHandler, + IN SOUND_THREAD_IO_COMPLETION_HANDLER IoCompletionHandler, IN LPVOID PrivateThreadData) { PSOUND_THREAD SoundThread = NULL; @@ -166,10 +193,14 @@ SoundThread->PrivateData = PrivateThreadData; SoundThread->Running = FALSE; SoundThread->Handle = INVALID_HANDLE_VALUE; + SoundThread->RequestHandler = RequestHandler; SoundThread->ReadyEvent = INVALID_HANDLE_VALUE; SoundThread->RequestEvent = INVALID_HANDLE_VALUE; SoundThread->DoneEvent = INVALID_HANDLE_VALUE; + + SoundThread->IoCompletionHandler = IoCompletionHandler; + SoundThread->FirstCompletedIo = NULL;
/* No need to initialise the requests */
@@ -293,3 +324,92 @@
return MMSYSERR_NOERROR; } + +VOID CALLBACK +CompleteSoundThreadIo( + IN DWORD dwErrorCode, + IN DWORD dwNumberOfBytesTransferred, + IN LPOVERLAPPED lpOverlapped) +{ + PSOUND_DEVICE_INSTANCE SoundDeviceInstance; + PSOUND_THREAD_OVERLAPPED SoundThreadOverlapped; + PSOUND_THREAD_COMPLETED_IO CompletionData; + + SoundThreadOverlapped = (PSOUND_THREAD_OVERLAPPED) lpOverlapped; + SOUND_ASSERT(SoundThreadOverlapped); + + CompletionData = SoundThreadOverlapped->CompletionData; + SOUND_ASSERT(CompletionData); + + SoundDeviceInstance = SoundThreadOverlapped->SoundDeviceInstance; + SOUND_ASSERT(SoundDeviceInstance); + SOUND_ASSERT(SoundDeviceInstance->Thread); + + SOUND_DEBUG(L"New I/O Completion Callback Called"); + + /* This is going at the start of the list */ + CompletionData->Next = SoundDeviceInstance->Thread->FirstCompletedIo; + SoundDeviceInstance->Thread->FirstCompletedIo = CompletionData; + + /* Whatever information was supplied to us originally by the caller */ + CompletionData->ContextData = SoundThreadOverlapped->ContextData; + + /* How much data was transferred */ + CompletionData->BytesTransferred = dwNumberOfBytesTransferred; + + /* Overlapped structure gets freed now, but we still need the completion */ + FreeMemory(SoundThreadOverlapped); + + SOUND_DEBUG(L"New I/O Completion Callback Done"); +} + +MMRESULT +OverlappedWriteToSoundDevice( + IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, + IN PVOID ContextData, + IN PVOID Buffer, + IN DWORD BufferSize) +{ + PSOUND_THREAD_OVERLAPPED Overlapped; + PSOUND_THREAD_COMPLETED_IO CompletedIo; + + if ( ! SoundDeviceInstance ) + return MMSYSERR_INVALPARAM; + + if ( ! SoundDeviceInstance->Thread ) + return MMSYSERR_ERROR; /* FIXME - better return code? */ + + if ( ! Buffer ) + return MMSYSERR_INVALPARAM; + + /* This will contain information about the write operation */ + Overlapped = AllocateMemoryFor(SOUND_THREAD_OVERLAPPED); + if ( ! Overlapped ) + return MMSYSERR_NOMEM; + + /* We collect this on I/O completion */ + CompletedIo = AllocateMemoryFor(SOUND_THREAD_COMPLETED_IO); + if ( ! CompletedIo ) + { + FreeMemory(Overlapped); + return MMSYSERR_NOMEM; + } + + ZeroMemory(Overlapped, sizeof(SOUND_THREAD_OVERLAPPED)); + ZeroMemory(CompletedIo, sizeof(SOUND_THREAD_COMPLETED_IO)); + + /* We'll want to know which device to queue completion data to */ + Overlapped->SoundDeviceInstance = SoundDeviceInstance; + + /* Caller-supplied data, gets passed back on completion */ + Overlapped->ContextData = ContextData; + + /* The completion data buffer which will be filled later */ + Overlapped->CompletionData = CompletedIo; + + return WriteSoundDeviceBuffer(SoundDeviceInstance, + Buffer, + BufferSize, + CompleteSoundThreadIo, + (LPOVERLAPPED) Overlapped); +}
Modified: branches/silverblade-audio/lib/drivers/sound/mmebuddy/wave/wavethread.c URL: http://svn.reactos.org/svn/reactos/branches/silverblade-audio/lib/drivers/so... ============================================================================== --- branches/silverblade-audio/lib/drivers/sound/mmebuddy/wave/wavethread.c [iso-8859-1] (original) +++ branches/silverblade-audio/lib/drivers/sound/mmebuddy/wave/wavethread.c [iso-8859-1] Sun Jul 6 18:12:29 2008 @@ -10,6 +10,7 @@
History: 4 July 2008 - Created + 6 July 2008 - Restructured to hide some of the low-level threading
TODO: Track if a buffer has already been inserted? @@ -20,35 +21,10 @@
#include <mmebuddy.h>
-MMRESULT -WriteWaveBufferToSoundDevice( - IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, - IN PWAVE_THREAD_DATA ThreadData, - IN PWAVEHDR WaveHeader); - - - -VOID CALLBACK -WaveBufferCompleted( - IN DWORD dwErrorCode, - IN DWORD dwNumberOfBytesTransferred, - IN LPOVERLAPPED lpOverlapped) -{ - PWAVE_OVERLAPPED WaveOverlapped = (PWAVE_OVERLAPPED) lpOverlapped; - /*PWAVE_THREAD_DATA ThreadData = WaveOverlapped->ThreadData;*/ - WCHAR msg[1024]; - - wsprintf(msg, L"Buffer %x done\nWrote %d bytes\nErrCode %d", - WaveOverlapped->Header, - dwNumberOfBytesTransferred, - dwErrorCode); - - MessageBox(0, msg, L"File IO Callback", MB_OK | MB_TASKMODAL); - - WriteWaveBufferToSoundDevice(WaveOverlapped->SoundDeviceInstance, - WaveOverlapped->ThreadData, - WaveOverlapped->Header); -} + +/* + Just a neat wrapper around the writing routine. +*/
MMRESULT WriteWaveBufferToSoundDevice( @@ -60,21 +36,16 @@ SOUND_ASSERT(ThreadData); SOUND_ASSERT(WaveHeader);
- /* Prepare our overlapped data */ - ZeroMemory(&ThreadData->Overlapped, sizeof(WAVE_OVERLAPPED)); - ThreadData->Overlapped.SoundDeviceInstance = SoundDeviceInstance; - ThreadData->Overlapped.ThreadData = ThreadData; - ThreadData->Overlapped.Header = WaveHeader; - - return WriteSoundDeviceBuffer(SoundDeviceInstance, - WaveHeader->lpData, - WaveHeader->dwBufferLength, - WaveBufferCompleted, - (LPOVERLAPPED) &ThreadData->Overlapped); -} - - -/* Internal dispatch routines */ + return OverlappedWriteToSoundDevice(SoundDeviceInstance, + (PVOID) WaveHeader, + WaveHeader->lpData, + WaveHeader->dwBufferLength); +} + + +/* + Private thread dispatch routines +*/
MMRESULT SubmitWaveBuffer( @@ -123,7 +94,9 @@ }
-/* Thread callback */ +/* + Thread request dispatcher +*/
MMRESULT ProcessWaveThreadRequest( @@ -155,6 +128,48 @@
return MMSYSERR_NOTSUPPORTED; } + + +/* + I/O completion + Called from outside the overlapped I/O completion APC +*/ + +VOID +ProcessWaveIoCompletion( + IN struct _SOUND_DEVICE_INSTANCE* Instance, + IN PVOID ContextData, + IN DWORD BytesTransferred) +{ + LPWAVEHDR WaveHeader = (LPWAVEHDR) ContextData; + + SOUND_DEBUG(L"ProcessWaveIoCompletion called :)"); + SOUND_DEBUG_HEX(WaveHeader); + + /* + At this point we know: + - The sound device instance involved in the transaction + - The wave header for the buffer which just completed + - How much data was transferred + + The next task is to figure out how much of the buffer + got played, and enqueue the next buffer if the current + one has been completed. + + Basically, this routine is responsible for keeping the + stream of audio data to/from the sound driver going. + + When no more WAVEHDRs are queued (ie, WaveHeader->lpNext + is NULL), no more buffers are submitted and the sound + thread will wait for more requests from the client before + it does anything else. + */ +} + + +/* + Wave thread start/stop routines +*/
MMRESULT StartWaveThread( @@ -181,6 +196,7 @@ /* Kick off the thread */ Result = StartSoundThread(Instance, ProcessWaveThreadRequest, + ProcessWaveIoCompletion, WaveThreadData); if ( Result != MMSYSERR_NOERROR ) {