Closing of wave output devices is functional and terminates the sound thread

cleanly. Started writing code to support pause/restart (nonfunctional yet.)
Stubbed mixer messages, added a readme.txt to give an overview of functions
supported. Also includes partial rewrite of wdmaud.drv. Currently I am
listening to DI.FM in ReactOS using an NT4 sndblst.sys along with ReactOS'
sndblst.dll


svn path=/trunk/; revision=39716
This commit is contained in:
Andrew Greenwood 2009-02-22 21:14:54 +00:00
parent 0fb477e07a
commit 141ec3a682
12 changed files with 662 additions and 361 deletions

View file

@ -93,6 +93,7 @@ BOOLEAN FoundDevice(
return FALSE;
/* Set up our function table */
ZeroMemory(&FuncTable, sizeof(MMFUNCTION_TABLE));
FuncTable.GetCapabilities = GetSoundBlasterDeviceCapabilities;
FuncTable.QueryWaveFormatSupport = QueryNt4WaveDeviceFormatSupport;
FuncTable.SetWaveFormat = SetNt4WaveDeviceFormat;

View file

@ -18,9 +18,268 @@
#include <mmddk.h>
#include <mmebuddy.h>
#include <ks.h>
#include <ksmedia.h>
#include "interface.h"
#define KERNEL_DEVICE_NAME L"\\\\Device\\wdmaud"
PWSTR UnknownWaveIn = L"Wave Input";
PWSTR UnknownWaveOut = L"Wave Output";
PWSTR UnknownMidiIn = L"Midi Input";
PWSTR UnknownMidiOut = L"Midi Output";
HANDLE KernelHandle = INVALID_HANDLE_VALUE;
DWORD OpenCount = 0;
MMRESULT
GetNumWdmDevs(
IN HANDLE Handle,
IN MMDEVICE_TYPE DeviceType,
OUT DWORD* DeviceCount)
{
MMRESULT Result;
WDMAUD_DEVICE_INFO DeviceInfo;
VALIDATE_MMSYS_PARAMETER( Handle != INVALID_HANDLE_VALUE );
VALIDATE_MMSYS_PARAMETER( IS_VALID_SOUND_DEVICE_TYPE(DeviceType) );
VALIDATE_MMSYS_PARAMETER( DeviceCount );
ZeroMemory(&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO));
DeviceInfo.DeviceType = DeviceType;
Result = SyncOverlappedDeviceIoControl(Handle,
IOCTL_GETNUMDEVS_TYPE,
(LPVOID) &DeviceInfo,
sizeof(WDMAUD_DEVICE_INFO),
(LPVOID) &DeviceInfo,
sizeof(WDMAUD_DEVICE_INFO),
NULL);
if ( ! Result )
{
*DeviceCount = 0;
return TranslateInternalMmResult(Result);
}
*DeviceCount = DeviceInfo.DeviceCount;
return MMSYSERR_NOERROR;
}
MMRESULT
GetWdmDeviceCapabilities(
IN PSOUND_DEVICE SoundDevice,
OUT PVOID Capabilities,
IN DWORD CapabilitiesSize)
{
/* NOTE - At this time, WDMAUD does not support this properly */
MMRESULT Result;
MMDEVICE_TYPE DeviceType;
SND_ASSERT( SoundDevice );
SND_ASSERT( Capabilities );
SND_TRACE(L"WDMAUD - GetWdmDeviceCapabilities\n");
Result = GetSoundDeviceType(SoundDevice, &DeviceType);
SND_ASSERT( Result == MMSYSERR_NOERROR );
if ( ! MMSUCCESS(Result) )
return Result;
/* This is pretty much a big hack right now */
switch ( DeviceType )
{
case WAVE_OUT_DEVICE_TYPE :
{
LPWAVEOUTCAPS WaveOutCaps = (LPWAVEOUTCAPS) Capabilities;
WaveOutCaps->wMid = 0;
WaveOutCaps->wPid = 0;
WaveOutCaps->vDriverVersion = 0x0001;
CopyWideString(WaveOutCaps->szPname, UnknownWaveOut);
/* HACK: We may not really support all formats! */
WaveOutCaps->dwFormats = 0xffffffff;
WaveOutCaps->wChannels = 2;
WaveOutCaps->dwSupport = 0;
break;
}
case WAVE_IN_DEVICE_TYPE :
{
LPWAVEINCAPS WaveInCaps = (LPWAVEINCAPS) Capabilities;
CopyWideString(WaveInCaps->szPname, UnknownWaveIn);
/* TODO... other fields */
break;
}
}
return MMSYSERR_NOERROR;
}
MMRESULT
OpenWdmSoundDevice(
IN struct _SOUND_DEVICE* SoundDevice, /* NOT USED */
OUT PVOID* Handle)
{
/* Only open this if it's not already open */
if ( KernelHandle == INVALID_HANDLE_VALUE )
{
KernelHandle = CreateFile(KERNEL_DEVICE_NAME,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
}
if ( KernelHandle == INVALID_HANDLE_VALUE )
return MMSYSERR_ERROR;
SND_ASSERT( Handle );
*Handle = KernelHandle;
++ OpenCount;
return MMSYSERR_NOERROR;
}
MMRESULT
CloseWdmSoundDevice(
IN struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance, /* NOT USED */
IN PVOID Handle)
{
SND_ASSERT( OpenCount > 0 );
SND_ASSERT( KernelHandle != INVALID_HANDLE_VALUE );
-- OpenCount;
if ( OpenCount < 1 )
{
CloseHandle(KernelHandle);
KernelHandle = INVALID_HANDLE_VALUE;
}
return MMSYSERR_NOERROR;
}
MMRESULT
QueryWdmWaveDeviceFormatSupport(
IN PSOUND_DEVICE Device,
IN PWAVEFORMATEX WaveFormat,
IN DWORD WaveFormatSize)
{
/* Whatever... */
return MMSYSERR_NOERROR;
}
MMRESULT
SetWdmWaveDeviceFormat(
IN PSOUND_DEVICE_INSTANCE Instance,
IN PWAVEFORMATEX WaveFormat,
IN DWORD WaveFormatSize)
{
MMRESULT Result;
PSOUND_DEVICE SoundDevice;
PVOID Identifier;
WDMAUD_DEVICE_INFO DeviceInfo;
Result = GetSoundDeviceFromInstance(Instance, &SoundDevice);
if ( ! MMSUCCESS(Result) )
{
return TranslateInternalMmResult(Result);
}
Result = GetSoundDeviceIdentifier(SoundDevice, &Identifier);
if ( ! MMSUCCESS(Result) )
{
return TranslateInternalMmResult(Result);
}
ZeroMemory(&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO));
DeviceInfo.u.WaveFormatEx.cbSize = WaveFormat->cbSize;
DeviceInfo.u.WaveFormatEx.wFormatTag = WaveFormat->wFormatTag;
DeviceInfo.u.WaveFormatEx.nChannels = WaveFormat->nChannels;
DeviceInfo.u.WaveFormatEx.nSamplesPerSec = WaveFormat->nSamplesPerSec;
DeviceInfo.u.WaveFormatEx.nBlockAlign = WaveFormat->nBlockAlign;
DeviceInfo.u.WaveFormatEx.nAvgBytesPerSec = WaveFormat->nAvgBytesPerSec;
DeviceInfo.u.WaveFormatEx.wBitsPerSample = WaveFormat->wBitsPerSample;
Result = SyncOverlappedDeviceIoControl(KernelHandle,
IOCTL_OPEN_WDMAUD,
(LPVOID) &DeviceInfo,
sizeof(WDMAUD_DEVICE_INFO),
(LPVOID) &DeviceInfo,
sizeof(WDMAUD_DEVICE_INFO),
NULL);
if ( ! MMSUCCESS(Result) )
{
return TranslateInternalMmResult(Result);
}
return MMSYSERR_NOERROR;
}
MMRESULT
PopulateWdmDeviceList(
HANDLE Handle,
MMDEVICE_TYPE DeviceType)
{
MMRESULT Result;
DWORD DeviceCount = 0;
PSOUND_DEVICE SoundDevice = NULL;
MMFUNCTION_TABLE FuncTable;
DWORD i;
VALIDATE_MMSYS_PARAMETER( Handle != INVALID_HANDLE_VALUE );
VALIDATE_MMSYS_PARAMETER( IS_VALID_SOUND_DEVICE_TYPE(DeviceType) );
Result = GetNumWdmDevs(Handle, DeviceType, &DeviceCount);
if ( ! MMSUCCESS(Result) )
{
SND_ERR(L"Error %d while obtaining number of devices\n", Result);
return TranslateInternalMmResult(Result);
}
SND_TRACE(L"%d devices of type %d found\n", DeviceCount, DeviceType);
for ( i = 0; i < DeviceCount; ++ i )
{
Result = ListSoundDevice(DeviceType, (PVOID) i, &SoundDevice);
if ( ! MMSUCCESS(Result) )
{
SND_ERR(L"Failed to list sound device - error %d\n", Result);
return TranslateInternalMmResult(Result);
}
/* Set up our function table */
ZeroMemory(&FuncTable, sizeof(MMFUNCTION_TABLE));
FuncTable.GetCapabilities = GetWdmDeviceCapabilities;
FuncTable.QueryWaveFormatSupport = QueryWdmWaveDeviceFormatSupport;
FuncTable.SetWaveFormat = SetWdmWaveDeviceFormat;
FuncTable.Open = OpenWdmSoundDevice;
FuncTable.Close = CloseWdmSoundDevice;
//FuncTable.CommitWaveBuffer = WriteFileEx_Committer;
SetSoundDeviceFunctionTable(SoundDevice, &FuncTable);
}
return MMSYSERR_NOERROR;
}
APIENTRY LONG
DriverProc(
@ -36,6 +295,7 @@ DriverProc(
{
case DRV_LOAD :
{
HANDLE Handle;
SND_TRACE(L"DRV_LOAD\n");
Result = InitEntrypointMutexes();
@ -43,24 +303,29 @@ DriverProc(
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);
OpenWdmSoundDevice(NULL, &Handle);
if ( KernelHandle == INVALID_HANDLE_VALUE )
if ( Handle == INVALID_HANDLE_VALUE )
{
SND_ERR(L"Failed to open %s\n", KERNEL_DEVICE_NAME);
CleanupEntrypointMutexes();
UnlistAllSoundDevices();
//UnlistAllSoundDevices();
return 0L;
}
/* Populate the device lists */
SND_TRACE(L"Populating device lists\n");
PopulateWdmDeviceList(KernelHandle, WAVE_OUT_DEVICE_TYPE);
PopulateWdmDeviceList(KernelHandle, WAVE_IN_DEVICE_TYPE);
PopulateWdmDeviceList(KernelHandle, MIDI_OUT_DEVICE_TYPE);
PopulateWdmDeviceList(KernelHandle, MIDI_IN_DEVICE_TYPE);
PopulateWdmDeviceList(KernelHandle, AUX_DEVICE_TYPE);
PopulateWdmDeviceList(KernelHandle, MIXER_DEVICE_TYPE);
CloseWdmSoundDevice(NULL, Handle);
SND_TRACE(L"Initialisation complete\n");
return 1L;

View file

@ -2,6 +2,7 @@
<importlibrary definition="wdmaud.spec" />
<include base="wdmaud.drv">.</include>
<include base="ReactOS">include/reactos/libs/sound</include>
<include base="wdmaud_kernel">.</include>
<define name="DEBUG_NT4" /><!-- Use custom debug routines -->
<library>mmebuddy</library>
<library>ntdll</library>

View file

@ -181,6 +181,7 @@ typedef struct _SOUND_OVERLAPPED
OVERLAPPED Standard;
struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance;
PWAVEHDR Header;
BOOL PerformCompletion;
} SOUND_OVERLAPPED, *PSOUND_OVERLAPPED;
typedef MMRESULT (*WAVE_COMMIT_FUNC)(
@ -314,8 +315,9 @@ typedef struct _SOUND_DEVICE_INSTANCE
};
PWAVEHDR WaveLoopStart;
PWAVEHDR CurrentWaveHeader;
//PWAVEHDR CurrentWaveHeader;
DWORD OutstandingBuffers;
DWORD LoopsRemaining;
} SOUND_DEVICE_INSTANCE, *PSOUND_DEVICE_INSTANCE;
/* This lives in WAVEHDR.reserved */
@ -383,6 +385,10 @@ MmeCloseDevice(
#define MmeWriteWaveHeader(private_handle, header) \
WriteWaveHeader((PSOUND_DEVICE_INSTANCE)private_handle, (PWAVEHDR)header)
MMRESULT
MmeResetWavePlayback(
IN DWORD PrivateHandle);
/*
capabilities.c
@ -619,31 +625,15 @@ WriteFileEx_Committer(
IN PSOUND_OVERLAPPED Overlap,
IN LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine);
MMRESULT
StopStreaming(
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance);
/*
kernel.c
*/
#if 0
#define QueryDevice(h, ctl, o, o_size, xfer, ovl) \
Win32ErrorToMmResult( \
DeviceIoControl(h, ctl, NULL, 0, o, o_size, xfer, ovl) != 0 \
? ERROR_SUCCESS : GetLastError() \
)
#define ControlDevice(h, ctl, i, i_size, xfer, ovl) \
Win32ErrorToMmResult( \
DeviceIoControl(h, ctl, i, i_size, NULL, 0, xfer, ovl) != 0 \
? ERROR_SUCCESS : GetLastError() \
)
#define QuerySoundDevice(sd, ctl, o, o_size, xfer) \
SoundDeviceIoControl(sd, ctl, NULL, 0, o, o_size, xfer)
#define ControlSoundDevice(sd, ctl, i, i_size, xfer) \
SoundDeviceIoControl(sd, ctl, i, i_size, NULL, 0, xfer)
#endif
MMRESULT
OpenKernelSoundDeviceByName(
IN PWSTR DevicePath,
@ -671,182 +661,4 @@ SyncOverlappedDeviceIoControl(
OUT LPDWORD BytesTransferred OPTIONAL);
#if 0
typedef UCHAR MMDEVICE_TYPE, *PMMDEVICE_TYPE;
struct _SOUND_DEVICE;
struct _SOUND_DEVICE_INSTANCE;
/*
Rather than pass caps structures around as a PVOID, this can be
used instead.
*/
typedef union _UNIVERSAL_CAPS
{
WAVEOUTCAPS WaveOut;
WAVEINCAPS WaveIn;
MIDIOUTCAPS MidiOut;
MIDIINCAPS MidiIn;
} UNIVERSAL_CAPS, *PUNIVERSAL_CAPS;
/* New sound thread code */
typedef MMRESULT (*SOUND_THREAD_REQUEST_HANDLER)(
IN struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance,
IN OPTIONAL PVOID Parameter);
typedef struct _SOUND_THREAD_REQUEST
{
/* The sound device instance this request relates to */
struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance;
/* What function to call */
SOUND_THREAD_REQUEST_HANDLER RequestHandler;
/* Caller-defined parameter */
PVOID Parameter;
/* This will contain the return code of the request function */
MMRESULT ReturnValue;
} SOUND_THREAD_REQUEST, *PSOUND_THREAD_REQUEST;
typedef VOID (*SOUND_THREAD_IO_COMPLETION_HANDLER)(
IN struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance,
IN PVOID Parameter OPTIONAL,
IN DWORD BytesWritten);
typedef struct _SOUND_THREAD_COMPLETED_IO
{
struct _SOUND_THREAD_COMPLETED_IO* Previous;
struct _SOUND_THREAD_COMPLETED_IO* Next;
struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance;
SOUND_THREAD_IO_COMPLETION_HANDLER CompletionHandler;
PVOID Parameter;
DWORD BytesTransferred;
} SOUND_THREAD_COMPLETED_IO, *PSOUND_THREAD_COMPLETED_IO;
typedef struct _SOUND_THREAD_OVERLAPPED
{
OVERLAPPED General;
/* Pointer to structure to fill with completion data */
PSOUND_THREAD_COMPLETED_IO CompletionData;
} SOUND_THREAD_OVERLAPPED, *PSOUND_THREAD_OVERLAPPED;
/*
Audio device function table
*/
typedef MMRESULT (*MMCREATEINSTANCE_FUNC)(
IN struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance);
typedef VOID (*MMDESTROYINSTANCE_FUNC)(
IN struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance);
typedef MMRESULT (*MMGETCAPS_FUNC)(
IN struct _SOUND_DEVICE* Device,
OUT PUNIVERSAL_CAPS Capabilities);
typedef MMRESULT (*MMWAVEQUERYFORMAT_FUNC)(
IN struct _SOUND_DEVICE* Device,
IN PWAVEFORMATEX WaveFormat,
IN DWORD WaveFormatSize);
typedef MMRESULT (*MMWAVESETFORMAT_FUNC)(
IN struct _SOUND_DEVICE_INSTANCE* Instance,
IN PWAVEFORMATEX WaveFormat,
IN DWORD WaveFormatSize);
typedef MMRESULT (*MMWAVEQUEUEBUFFER_FUNC)(
IN struct _SOUND_DEVICE_INSTANCE* Instance,
IN PWAVEHDR WaveHeader);
typedef MMRESULT (*MMGETWAVESTATE_FUNC)(
IN struct _SOUND_DEVICE_INSTANCE* Instance,
OUT PULONG State);
typedef MMRESULT (*MMSETWAVESTATE_FUNC)(
IN struct _SOUND_DEVICE_INSTANCE* Instance);
typedef struct _MMFUNCTION_TABLE
{
MMCREATEINSTANCE_FUNC Constructor;
MMDESTROYINSTANCE_FUNC Destructor;
MMGETCAPS_FUNC GetCapabilities;
MMWAVEQUERYFORMAT_FUNC QueryWaveFormat;
MMWAVESETFORMAT_FUNC SetWaveFormat;
MMWAVEQUEUEBUFFER_FUNC QueueWaveBuffer;
MMGETWAVESTATE_FUNC GetWaveDeviceState;
MMSETWAVESTATE_FUNC PauseWaveDevice;
MMSETWAVESTATE_FUNC RestartWaveDevice;
MMSETWAVESTATE_FUNC ResetWaveDevice;
MMSETWAVESTATE_FUNC BreakWaveDeviceLoop;
} MMFUNCTION_TABLE, *PMMFUNCTION_TABLE;
/*
Represents an audio device
*/
#define SOUND_DEVICE_TAG "SndD"
typedef struct _SOUND_DEVICE
{
struct _SOUND_DEVICE* Next;
struct _SOUND_DEVICE_INSTANCE* FirstInstance;
UCHAR DeviceType;
LPWSTR DevicePath;
MMFUNCTION_TABLE Functions;
} SOUND_DEVICE, *PSOUND_DEVICE;
/*
Represents an individual instance of an audio device
*/
#define WAVE_STREAM_INFO_TAG "WavS"
typedef struct _WAVE_STREAM_INFO
{
/* Buffer queue head and tail */
PWAVEHDR BufferQueueHead;
PWAVEHDR BufferQueueTail;
/* The buffer currently being processed */
PWAVEHDR CurrentBuffer;
/* How far into the current buffer we've gone */
DWORD BufferOffset;
/* How many I/O operations have been submitted */
DWORD BuffersOutstanding;
/* Looping */
PWAVEHDR LoopHead;
DWORD LoopsRemaining;
} WAVE_STREAM_INFO, *PWAVE_STREAM_INFO;
#define SOUND_DEVICE_INSTANCE_TAG "SndI"
typedef struct _SOUND_DEVICE_INSTANCE
{
struct _SOUND_DEVICE_INSTANCE* Next;
PSOUND_DEVICE Device;
/* The currently opened handle to the device */
HANDLE Handle;
/* PSOUND_THREAD Thread;*/
/* Device-specific parameters */
union
{
WAVE_STREAM_INFO Wave;
} Streaming;
} SOUND_DEVICE_INSTANCE, *PSOUND_DEVICE_INSTANCE;
#endif
#endif

View file

@ -37,8 +37,12 @@ VOID
FreeSoundDeviceInstance(
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
{
/* This won't work as the device is no longer valid by this point! */
/*SND_ASSERT( IsValidSoundDeviceInstance(SoundDeviceInstance) );*/
/*
Device is marked as invalid by now, but we can still do some sanity
checking.
*/
SND_ASSERT( SoundDeviceInstance->Thread == NULL );
ZeroMemory(SoundDeviceInstance, sizeof(SOUND_DEVICE_INSTANCE));
FreeMemory(SoundDeviceInstance);
}
@ -190,9 +194,9 @@ CreateSoundDeviceInstance(
(*SoundDeviceInstance)->HeadWaveHeader = NULL;
(*SoundDeviceInstance)->TailWaveHeader = NULL;
(*SoundDeviceInstance)->CurrentWaveHeader = NULL;
(*SoundDeviceInstance)->OutstandingBuffers = 0;
// TODO: Loop
(*SoundDeviceInstance)->LoopsRemaining = 0;
/* Create the streaming thread (TODO - is this for wave only?) */
Result = CreateSoundThread(&(*SoundDeviceInstance)->Thread);
@ -255,13 +259,26 @@ DestroySoundDeviceInstance(
return MMSYSERR_NOTSUPPORTED;
}
/* Stop the streaming thread (TODO - is this for wave only?) */
Result = DestroySoundThread(SoundDeviceInstance->Thread);
SND_ASSERT( MMSUCCESS(Result) ); /* It should succeed! */
if ( ! MMSUCCESS(Result ) )
{
return TranslateInternalMmResult(Result);
}
/* Blank this out here */
SoundDeviceInstance->Thread = NULL;
/* Try and close the device */
Result = FunctionTable->Close(SoundDeviceInstance, Handle);
SND_ASSERT( MMSUCCESS(Result) ); /* It should succeed! */
if ( ! MMSUCCESS(Result) )
return TranslateInternalMmResult(Result);
/* Drop it from the list */
Result = UnlistSoundDeviceInstance(SoundDeviceInstance);
SND_ASSERT( MMSUCCESS(Result) ); /* It should succeed! */
if ( ! MMSUCCESS(Result) )
return TranslateInternalMmResult(Result);
@ -274,7 +291,19 @@ MMRESULT
DestroyAllSoundDeviceInstances(
IN PSOUND_DEVICE SoundDevice)
{
return MMSYSERR_NOTSUPPORTED;
MMRESULT Result;
PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
SoundDeviceInstance = SoundDevice->HeadInstance;
while ( SoundDeviceInstance )
{
Result = DestroySoundDeviceInstance(SoundDeviceInstance);
SND_ASSERT( MMSUCCESS(Result) );
SoundDeviceInstance = SoundDeviceInstance->Next;
}
return MMSYSERR_NOERROR;
}
MMRESULT

View file

@ -51,6 +51,41 @@ mxdMessage(
Parameter2);
break;
}
case MXDM_INIT :
{
break;
}
case MXDM_OPEN :
{
break;
}
case MXDM_CLOSE :
{
break;
}
case MXDM_GETCONTROLDETAILS :
{
break;
}
case MXDM_SETCONTROLDETAILS :
{
break;
}
case MXDM_GETLINECONTROLS :
{
break;
}
case MXDM_GETLINEINFO :
{
break;
}
}
SND_TRACE(L"mxdMessage returning MMRESULT %d\n", Result);

View file

@ -164,6 +164,9 @@ MmeCloseDevice(
VALIDATE_MMSYS_PARAMETER( PrivateHandle );
SoundDeviceInstance = (PSOUND_DEVICE_INSTANCE) PrivateHandle;
if ( ! IsValidSoundDeviceInstance(SoundDeviceInstance) )
return MMSYSERR_INVALHANDLE;
Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
if ( ! MMSUCCESS(Result) )
return TranslateInternalMmResult(Result);
@ -172,7 +175,11 @@ MmeCloseDevice(
if ( ! MMSUCCESS(Result) )
return TranslateInternalMmResult(Result);
/* TODO: Check device is stopped! */
ReleaseEntrypointMutex(DeviceType);
/* TODO: Work with MIDI devices too */
NotifyMmeClient(SoundDeviceInstance,
DeviceType == WAVE_OUT_DEVICE_TYPE ? WOM_CLOSE : WIM_CLOSE,
0);
@ -182,3 +189,17 @@ MmeCloseDevice(
return Result;
}
MMRESULT
MmeResetWavePlayback(
IN DWORD PrivateHandle)
{
PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
SND_TRACE(L"Resetting wave device (WODM_RESET)\n");
VALIDATE_MMSYS_PARAMETER( PrivateHandle );
SoundDeviceInstance = (PSOUND_DEVICE_INSTANCE) PrivateHandle;
return StopStreaming(SoundDeviceInstance);
}

View file

@ -0,0 +1,90 @@
MME BUDDY
This library currently is capable of maintaining lists of devices for all of
the MME types, it will provide the appropriate entrypoints for each device
type, and code using this library simply needs to inform the MME Buddy
library of the devices that exist, and provide callback routines to be used
when opening/closing/playing, etc.
Code using this library needs to provide its own DriverProc entrypoint (this
may be refactored in future so that simply an init/cleanup routine need be
provided.)
WAVE OUTPUT
===========
Supported MME messages:
* WODM_GETNUMDEVS (Get number of devices)
* WODM_GETDEVCAPS (Get device capabilities)
* WODM_OPEN (Open a device, query supported formats)
* WODM_CLOSE (Close a device)
* WODM_PREPARE (Prepare a wave header)
* WODM_UNPREPARE (Unprepare a wave header)
* WODM_WRITE (Submit a prepared header to be played)
Unsupported MME messages:
* Any not mentioned above
Notes/Bugs:
* WHDR_BEGINLOOP and WHDR_ENDLOOP are ignored
* Not possible to pause/restart playback
WAVE INPUT
==========
Supported MME messages:
* WIDM_GETNUMDEVS (Get number of devices)
Unsupported MME messages:
* Any not mentioned above
Notes/Bugs:
* Mostly unimplemented
MIDI OUTPUT
===========
Supported MME messages:
* MODM_GETNUMDEVS (Get number of devices)
Unsupported MME messages:
* Any not mentioned above
Notes/Bugs:
* Mostly unimplemented
MIDI INPUT
==========
Supported MME messages:
* MIDM_GETNUMDEVS (Get number of devices)
Unsupported MME messages:
* Any not mentioned above
Notes/Bugs:
* Mostly unimplemented
AUXILIARY
=========
Supported MME messages:
* AUXM_GETNUMDEVS (Get number of devices)
Unsupported MME messages:
* Any not mentioned above
Notes/Bugs:
* Mostly unimplemented
MIXER
=====
Supported MME messages:
* MXDM_GETNUMDEVS (Get number of devices)
Unsupported MME messages:
* Any not mentioned above
Notes/Bugs:
* Mostly unimplemented

View file

@ -56,7 +56,7 @@ SoundThreadMain(
else if ( WaitResult == WAIT_IO_COMPLETION )
{
SND_TRACE(L"SoundThread - Processing IO completion\n");
/* TODO */
/* TODO? What do we do here? Stream stuff? */
}
else
{
@ -66,9 +66,95 @@ SoundThreadMain(
}
SND_TRACE(L"Sound thread terminated\n");
return 0;
}
MMRESULT
CallSoundThread(
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
IN SOUND_THREAD_REQUEST_HANDLER RequestHandler,
IN PVOID Parameter OPTIONAL)
{
PSOUND_THREAD Thread;
VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
VALIDATE_MMSYS_PARAMETER( RequestHandler );
Thread = SoundDeviceInstance->Thread;
SND_TRACE(L"Waiting for READY event\n");
WaitForSingleObject(Thread->Events.Ready, INFINITE);
Thread->Request.Result = MMSYSERR_NOTSUPPORTED;
Thread->Request.Handler = RequestHandler;
Thread->Request.SoundDeviceInstance = SoundDeviceInstance;
Thread->Request.Parameter = Parameter;
/* Notify the thread it has work to do */
SND_TRACE(L"Setting REQUEST event\n");
SetEvent(Thread->Events.Request);
/* Wait for the work to be done */
SND_TRACE(L"Waiting for DONE event\n");
WaitForSingleObject(Thread->Events.Done, INFINITE);
return Thread->Request.Result;
}
MMRESULT
SoundThreadTerminator(
IN PSOUND_DEVICE_INSTANCE Instance,
IN PVOID Parameter)
{
PSOUND_THREAD Thread = (PSOUND_THREAD) Parameter;
SND_TRACE(L"Sound thread terminator routine called\n");
SND_ASSERT( Thread );
Thread->Running = FALSE;
return MMSYSERR_NOERROR;
}
MMRESULT
TerminateSoundThread(
IN PSOUND_THREAD Thread)
{
DWORD WaitResult;
SND_ASSERT( Thread );
SND_TRACE(L"Waiting for READY event\n");
WaitForSingleObject(Thread->Events.Ready, INFINITE);
Thread->Request.Result = MMSYSERR_NOTSUPPORTED;
Thread->Request.Handler = SoundThreadTerminator;
Thread->Request.SoundDeviceInstance = NULL;
Thread->Request.Parameter = (PVOID) Thread;
/* Notify the thread it has work to do */
SND_TRACE(L"Setting REQUEST event\n");
SetEvent(Thread->Events.Request);
/* Wait for the work to be done */
SND_TRACE(L"Waiting for DONE event\n");
WaitForSingleObject(Thread->Events.Done, INFINITE);
/* Wait for the thread to actually end */
WaitResult = WaitForSingleObject(Thread->Handle, INFINITE);
SND_ASSERT( WaitResult == WAIT_OBJECT_0 );
/* Close the thread and invalidate the handle */
CloseHandle(Thread->Handle); /* Is this needed? */
Thread->Handle = INVALID_HANDLE_VALUE;
return MMSYSERR_NOERROR;
}
MMRESULT
CreateSoundThreadEvents(
OUT HANDLE* ReadyEvent,
@ -195,39 +281,28 @@ DestroySoundThread(
IN PSOUND_THREAD Thread)
{
VALIDATE_MMSYS_PARAMETER( Thread );
SND_ASSERT( Thread->Handle != INVALID_HANDLE_VALUE );
SND_TRACE(L"Terminating sound thread\n");
Thread->Running = FALSE;
/* TODO: Implement me! Wait for thread to have finished? */
return MMSYSERR_NOTSUPPORTED;
/* Tell the thread to terminate itself */
TerminateSoundThread(Thread);
SND_TRACE(L"Sound thread terminated, performing cleanup of thread resources\n");
CloseHandle(Thread->Handle); /* Is this needed? */
Thread->Handle = INVALID_HANDLE_VALUE;
DestroySoundThreadEvents(Thread->Events.Ready,
Thread->Events.Request,
Thread->Events.Done);
/* Wipe and free the memory used for the thread */
ZeroMemory(Thread, sizeof(SOUND_THREAD));
FreeMemory(Thread);
SND_TRACE(L"Finished thread cleanup\n");
return MMSYSERR_NOERROR;
}
MMRESULT
CallSoundThread(
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
IN SOUND_THREAD_REQUEST_HANDLER RequestHandler,
IN PVOID Parameter OPTIONAL)
{
VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance );
VALIDATE_MMSYS_PARAMETER( RequestHandler );
/* TODO: Don't call this directly? */
PSOUND_THREAD Thread = SoundDeviceInstance->Thread;
SND_TRACE(L"Waiting for READY event\n");
WaitForSingleObject(Thread->Events.Ready, INFINITE);
Thread->Request.Result = MMSYSERR_NOTSUPPORTED;
Thread->Request.Handler = RequestHandler;
Thread->Request.SoundDeviceInstance = SoundDeviceInstance;
Thread->Request.Parameter = Parameter;
/* Notify the thread it has work to do */
SND_TRACE(L"Setting REQUEST event\n");
SetEvent(Thread->Events.Request);
/* Wait for the work to be done */
SND_TRACE(L"Waiting for DONE event\n");
WaitForSingleObject(Thread->Events.Done, INFINITE);
return Thread->Request.Result;
}

View file

@ -235,7 +235,7 @@ EnqueueWaveHeader(
/* Set the "in queue" flag */
WaveHeader->dwFlags |= WHDR_INQUEUE;
if ( ! SoundDeviceInstance->TailWaveHeader )
if ( ! SoundDeviceInstance->HeadWaveHeader )
{
/* This is the first header in the queue */
SND_TRACE(L"Enqueued first wave header\n");
@ -327,10 +327,12 @@ CompleteWaveHeader(
}
/* 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);

View file

@ -34,6 +34,7 @@ DoWaveStreaming(
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
{
MMRESULT Result;
MMDEVICE_TYPE DeviceType;
PSOUND_DEVICE SoundDevice;
PMMFUNCTION_TABLE FunctionTable;
PWAVEHDR Header;
@ -42,6 +43,9 @@ DoWaveStreaming(
Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
SND_ASSERT( MMSUCCESS(Result) );
Result = GetSoundDeviceType(SoundDevice, &DeviceType);
SND_ASSERT( MMSUCCESS(Result) );
Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
SND_ASSERT( MMSUCCESS(Result) );
SND_ASSERT( FunctionTable );
@ -72,9 +76,13 @@ DoWaveStreaming(
/* Can never be *above* the length */
SND_ASSERT( HeaderExtension->BytesCommitted <= Header->dwBufferLength );
/* Is this header entirely committed? */
if ( HeaderExtension->BytesCommitted == Header->dwBufferLength )
{
Header = Header->lpNext;
{
/* Move on to the next header */
Header = Header->lpNext;
}
}
else
{
@ -106,6 +114,10 @@ DoWaveStreaming(
Overlap->SoundDeviceInstance = SoundDeviceInstance;
Overlap->Header = Header;
/* Don't complete this header if it's part of a loop */
Overlap->PerformCompletion = TRUE;
// ( SoundDeviceInstance->LoopsRemaining > 0 );
/* Adjust the commit-related counters */
HeaderExtension->BytesCommitted += BytesToCommit;
++ SoundDeviceInstance->OutstandingBuffers;
@ -128,34 +140,6 @@ DoWaveStreaming(
}
}
}
#if 0
// 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;
#endif
}
@ -183,10 +167,13 @@ CompleteIO(
IN DWORD dwNumberOfBytesTransferred,
IN LPOVERLAPPED lpOverlapped)
{
MMDEVICE_TYPE DeviceType;
PSOUND_DEVICE SoundDevice;
PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
PSOUND_OVERLAPPED SoundOverlapped = (PSOUND_OVERLAPPED) lpOverlapped;
PWAVEHDR WaveHdr;
PWAVEHDR_EXTENSION HdrExtension;
MMRESULT Result;
WaveHdr = (PWAVEHDR) SoundOverlapped->Header;
SND_ASSERT( WaveHdr );
@ -196,13 +183,21 @@ CompleteIO(
SoundDeviceInstance = SoundOverlapped->SoundDeviceInstance;
Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
SND_ASSERT( MMSUCCESS(Result) );
Result = GetSoundDeviceType(SoundDevice, &DeviceType);
SND_ASSERT( MMSUCCESS(Result) );
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 )
/* Did we finish a WAVEHDR and aren't looping? */
if ( HdrExtension->BytesCompleted == WaveHdr->dwBufferLength &&
SoundOverlapped->PerformCompletion )
{
CompleteWaveHeader(SoundDeviceInstance, WaveHdr);
}
@ -214,83 +209,6 @@ CompleteIO(
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,
@ -315,3 +233,48 @@ WriteFileEx_Committer(
return MMSYSERR_NOERROR;
}
/*
Stream control functions
(External/internal thread pairs)
TODO - Move elsewhere as these shouldn't be wave specific!
*/
MMRESULT
StopStreamingInSoundThread(
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
IN PVOID Parameter)
{
/* TODO */
return MMSYSERR_NOTSUPPORTED;
}
MMRESULT
StopStreaming(
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
{
MMRESULT Result;
PSOUND_DEVICE SoundDevice;
MMDEVICE_TYPE DeviceType;
if ( ! IsValidSoundDeviceInstance(SoundDeviceInstance) )
return MMSYSERR_INVALHANDLE;
Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
if ( ! MMSUCCESS(Result) )
return TranslateInternalMmResult(Result);
Result = GetSoundDeviceType(SoundDevice, &DeviceType);
if ( ! MMSUCCESS(Result) )
return TranslateInternalMmResult(Result);
/* FIXME: What about wave input? */
if ( DeviceType != WAVE_OUT_DEVICE_TYPE )
return MMSYSERR_NOTSUPPORTED;
return CallSoundThread(SoundDeviceInstance,
StopStreamingInSoundThread,
NULL);
}

View file

@ -98,14 +98,21 @@ wodMessage(
break;
}
case WODM_RESET :
{
/* Stop playback, reset position to zero */
Result = MmeResetWavePlayback(PrivateHandle);
break;
}
case WODM_RESTART :
{
/* Continue playback when paused */
break;
}
case WODM_GETPOS :
{
#if 0
/* Hacky code to test the threading */
PSOUND_DEVICE_INSTANCE Instance = (PSOUND_DEVICE_INSTANCE)PrivateHandle;
CallSoundThread(Instance->Thread, HelloWorld, Instance, L"Hello World!");
CallSoundThread(Instance->Thread, HelloWorld, Instance, L"Hello Universe!");
#endif
break;
}
}