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/react…
==============================================================================
--- 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/s…
==============================================================================
--- 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/s…
==============================================================================
--- 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 )
{