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
This commit is contained in:
Andrew Greenwood 2009-02-18 19:10:02 +00:00
parent c15dfd0010
commit 87dcb169be
10 changed files with 679 additions and 255 deletions

View file

@ -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);

View file

@ -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 <stdarg.h>
#include <windows.h>
#include <mmsystem.h>
#include <ntddsnd.h>
#include <sndtypes.h>
#include <mmddk.h>
#include <mmreg.h>
#include <debug.h>
#include <mmebuddy.h>
DWORD APIENTRY
mxdMessage(UINT uDevice,
UINT uMsg,
DWORD dwUser,
DWORD dwParam1,
DWORD dwParam2)
#define KERNEL_DEVICE_NAME L"\\\\Device\\wdmaud"
HANDLE KernelHandle = INVALID_HANDLE_VALUE;
APIENTRY LONG
DriverProc(
DWORD DriverId,
HANDLE DriverHandle,
UINT Message,
LONG Parameter1,
LONG Parameter2)
{
DPRINT1("mxdMessage(%04X, %04X, %08X, %08X, %08X);\n", uDevice, uMsg, dwUser, dwParam1, dwParam2);
MMRESULT Result;
switch (uMsg)
switch ( Message )
{
case MXDM_INIT:
break;
case DRV_LOAD :
{
SND_TRACE(L"DRV_LOAD\n");
case MXDM_GETNUMDEVS:
break;
Result = InitEntrypointMutexes();
case MXDM_GETDEVCAPS:
break;
if ( ! MMSUCCESS(Result) )
return 0L;
case MXDM_OPEN:
break;
KernelHandle = CreateFile(KERNEL_DEVICE_NAME,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_WRITE, // ok?
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
case MXDM_CLOSE:
break;
if ( KernelHandle == INVALID_HANDLE_VALUE )
{
SND_ERR(L"Failed to open %s", KERNEL_DEVICE_NAME);
CleanupEntrypointMutexes();
case MXDM_GETLINEINFO:
break;
UnlistAllSoundDevices();
case MXDM_GETLINECONTROLS:
break;
return 0L;
}
case MXDM_GETCONTROLDETAILS:
break;
SND_TRACE(L"Initialisation complete\n");
case MXDM_SETCONTROLDETAILS:
break;
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
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)
BOOL WINAPI DllMain(
HINSTANCE hinstDLL,
DWORD fdwReason,
LPVOID lpvReserved)
{
switch ( fdwReason )
{
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;
case WODM_OPEN:
break;
case WODM_CLOSE:
break;
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)
{
DPRINT1("widMessage(%04X, %04X, %08X, %08X, %08X);\n", uDevice, uMsg, dwUser, dwParam1, dwParam2);
switch (uMsg)
{
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;
}
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)
{
switch (dwReason)
{
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;
}

View file

@ -1,12 +1,14 @@
<module name="wdmaud.drv" type="win32dll" baseaddress="${BASEADDRESS_WDMAUD}" installbase="system32" installname="wdmaud.drv">
<module name="wdmaud.drv" type="win32dll" baseaddress="${BASEADDRESS_WDMAUD}" installbase="system32" installname="wdmaud.drv" unicode="yes">
<importlibrary definition="wdmaud.spec" />
<include base="wdmaud.drv">.</include>
<define name="_DISABLE_TIDENTS" />
<library>advapi32</library>
<include base="ReactOS">include/reactos/libs/sound</include>
<define name="DEBUG_NT4" /><!-- Use custom debug routines -->
<library>mmebuddy</library>
<library>ntdll</library>
<library>kernel32</library>
<library>winmm</library>
<library>user32</library>
<library>winmm</library>
<library>advapi32</library>
<file>wdmaud.c</file>
<file>wdmaud.rc</file>
</module>

View file

@ -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
*/

View file

@ -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) )

View file

@ -16,7 +16,8 @@
<file>widMessage.c</file>
<file>wodMessage.c</file>
<file>format.c</file>
<file>header.c</file>
<file>header.c</file>
<file>streaming.c</file>
</directory>
<directory name="midi">
<file>midMessage.c</file>

View file

@ -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 <mmddk.h>
#include <ntddsnd.h>
#include <mmebuddy.h>
#include <sndtypes.h>
/*
@ -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;
return WaveHeaderOperation(FunctionTable->PrepareWaveHeader,
SoundDeviceInstance,
Header);
Header->reserved = (DWORD_PTR) Extension;
Extension->BytesCommitted = 0;
Extension->BytesCompleted = 0;
/* Configure the flags */
Header->dwFlags |= WHDR_PREPARED;
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 )
{
return Result;
/* 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
{
/* 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);
}

View file

@ -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 <windows.h>
#include <mmsystem.h>
#include <mmddk.h>
#include <ntddsnd.h>
#include <mmebuddy.h>
#include <sndtypes.h>
/*
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;
}

View file

@ -94,7 +94,7 @@ wodMessage(
case WODM_WRITE :
{
Result = MmeEnqueueWaveHeader(PrivateHandle, Parameter1);
Result = MmeWriteWaveHeader(PrivateHandle, Parameter1);
break;
}

View file

@ -8,6 +8,8 @@
* PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
*/
#define NDEBUG
#include <windows.h>
#include <mmsystem.h>
#include <mmddk.h>