Cleanup of existing code, basic implementation of play/stop/resume states.

svn path=/trunk/; revision=19954
This commit is contained in:
Andrew Greenwood 2005-12-07 18:37:49 +00:00
parent 2e8b6ad70c
commit 0638246fcd
11 changed files with 1367 additions and 673 deletions

97
reactos/lib/wdmaud/README.TXT Executable file
View file

@ -0,0 +1,97 @@
User-mode Multimedia Driver for WDM Audio
-----------------------------------------
USAGE
-----
This is a "drop-in" replacement for the Windows XP wdmaud.drv component. To
make use of it, you'll need to disable system file protection somehow (easy
way is to rename all *.cab files in the sub-folders of C:\WINDOWS\DRIVER CACHE
to something else, then delete or rename WDMAUD.DRV in both C:\WINDOWS\SYSTEM32
and C:\WINDOWS\SYSTEM32\DLLCACHE.)
Now put the ReactOS wdmaud.drv in C:\WINDOWS\SYSTEM32. At some point, you'll
be asked to insert the Windows CD as some files are missing - cancel this and
choose "yes" when asked if you're sure. This is due to the system file
protection not being able to have its own way.
That should be all there is to it.
IMPLEMENTATION/DEVELOPMENT NOTES
--------------------------------
The style of driver used here dates back to the days of Windows 3.1. Not much
has changed - the NT 4 Sound Blaster driver exported the same functions and
supported the same message codes as the traditional Windows 3.1 user-mode
drivers did.
But in XP, something strange happens with WDMAUD (which is why you can't just
put this next to the existing WDMAUD under a different name!)
It appears that WINMM.DLL treats WDMAUD.DRV differently to other drivers.
Here's a summary of how things work differently:
1) It seems DRV_ENABLE and DRV_DISABLE are the only important messages
processed by DriverProc. These open and close the kernel-mode
driver.
2) Each message handling function (aside from DriverProc) receives a
DRVM_INIT message after the driver has been opened. The second
parameter of this is a pointer to a string containing a device
path (\\?\... format.) Returning zero seems to be the accepted
thing to do, in any case.
The purpose of this function (in our case) is to allow WDMAUD.DRV
to let WDMAUD.SYS know which device we want to play with.
Presumably, this is called when new devices are added to the system,
as well. I don't know if this is called once per hardware device...
3) xxxx_GETNUMDEVS now has extra data passed to it! The first
parameter is a device path string - which will have been passed
to DRVM_INIT previously.
4) xxxx_GETDEVCAPS is a bit hazardous. The old set of parameters were:
1 - Pointer to a capabilities structure (eg: WAVEOUTCAPS)
2 - Size of the above structure
But now, the parameters are:
1 - Pointer to a MDEVCAPSEX struct (which points to a regular
capabilities structure)
2 - Device path string
So anything expecting the second parameter to be a size (for copying
memory maybe) is in for a bit of a surprise there!
The reason for the above changes is Plug and Play. It seems that the extra
functionality was added in Windows 98 (possibly 95 as well) to make it
possible to hot-swap winmm-supported devices without requiring a restart of
whatever applications are using the devices.
That's the theory, at least.
TODO
----
Our WINMM.DLL will need hacking to make sure it can take into account the
preferential treatment given to WDMAUD.DRV
I'm not sure if it'll work with it yet as there seems to be some accomodation
for the PnP DRVM_INIT and DRVM_EXIT messages, but whether or not winmm will
function correctly with this WDMAUD.DRV replacement is something that remains
to be seen.
THANKS
------
Thanks to everyone who has encouraged me to continue developing the audio
system for ReactOS.
In particular, I'd like to thank Alex Ionescu for the many hours of assistance
he has given me in figuring out how things are done.
-
Andrew Greenwood
andrew.greenwood AT silverblade DOT co DOT uk

View file

@ -13,8 +13,13 @@
#include "wdmaud.h"
/*
TODO:
Make these work for the other device types!
StartDevice
Creates a completion thread for a device, sets the "is_running" member
of the device state to "true", and tells the kernel device to start
processing audio/MIDI data.
Wave devices always start paused.
*/
MMRESULT StartDevice(PWDMAUD_DEVICE_INFO device)
@ -25,21 +30,165 @@ MMRESULT StartDevice(PWDMAUD_DEVICE_INFO device)
result = ValidateDeviceInfoAndState(device);
if ( result != MMSYSERR_NOERROR )
{
DPRINT1("Device info/state not valid\n");
return result;
}
ioctl_code = device == WDMAUD_WAVE_IN ? IOCTL_WDMAUD_WAVE_IN_START :
device == WDMAUD_WAVE_OUT ? IOCTL_WDMAUD_WAVE_OUT_START :
0x0000;
ioctl_code =
IsWaveInDeviceType(device->type) ? IOCTL_WDMAUD_WAVE_IN_START :
IsWaveOutDeviceType(device->type) ? IOCTL_WDMAUD_WAVE_OUT_START :
IsMidiInDeviceType(device->type) ? IOCTL_WDMAUD_MIDI_IN_START :
0x0000;
ASSERT( ioctl_code );
result = CreateCompletionThread(device);
if ( MM_FAILURE( result ) )
{
DPRINT1("Failed to create completion thread\n");
return result;
}
device->state->is_running = TRUE;
result = CallKernelDevice(device, ioctl_code, 0, 0);
if ( MM_FAILURE( result ) )
{
DPRINT1("Audio could not be started\n");
return result;
}
if ( ! IsWaveDeviceType(device->type) )
device->state->is_paused = FALSE;
return result;
}
MMRESULT StopDevice(PWDMAUD_DEVICE_INFO device)
{
MMRESULT result;
DWORD ioctl_code;
result = ValidateDeviceInfoAndState(device);
if ( result != MMSYSERR_NOERROR )
{
DPRINT1("Device info/state not valid\n");
return result;
}
ioctl_code =
IsWaveInDeviceType(device->type) ? IOCTL_WDMAUD_WAVE_IN_STOP :
IsWaveOutDeviceType(device->type) ? IOCTL_WDMAUD_WAVE_OUT_STOP :
IsMidiInDeviceType(device->type) ? IOCTL_WDMAUD_MIDI_IN_STOP :
0x0000;
ASSERT( ioctl_code );
if ( IsMidiInDeviceType(device->type) )
{
EnterCriticalSection(device->state->device_queue_guard);
if ( ! device->state->is_running )
{
/* TODO: Free the MIDI data queue */
}
else
{
device->state->is_running = FALSE;
}
LeaveCriticalSection(device->state->device_queue_guard);
}
else /* wave device */
{
device->state->is_paused = TRUE;
}
result = CallKernelDevice(device, ioctl_code, 0, 0);
if ( MM_FAILURE( result ) )
{
DPRINT1("Audio could not be stopped\n");
return result;
}
if ( IsWaveDeviceType(device-type) )
{
device->state->is_paused = TRUE;
}
else /* MIDI Device */
{
/* TODO: Destroy completion thread etc. */
}
return result;
}
MMRESULT PauseDevice(PWDMAUD_DEVICE_INFO device)
MMRESULT ResetDevice(PWDMAUD_DEVICE_INFO device)
{
MMRESULT result;
DWORD ioctl_code;
result = ValidateDeviceInfoAndState(device);
if ( result != MMSYSERR_NOERROR )
{
DPRINT1("Device info/state not valid\n");
return result;
}
ioctl_code =
IsWaveInDeviceType(device->type) ? IOCTL_WDMAUD_WAVE_IN_RESET :
IsWaveOutDeviceType(device->type) ? IOCTL_WDMAUD_WAVE_OUT_RESET :
IsMidiInDeviceType(device->type) ? IOCTL_WDMAUD_MIDI_IN_RESET :
0x0000;
ASSERT( ioctl_code );
if ( IsMidiInDeviceType(device->type) )
{
EnterCriticalSection(device->state->device_queue_guard);
if ( ! device->state->is_running )
{
/* TODO: Free the MIDI data queue */
}
else
{
device->state->is_running = FALSE;
}
LeaveCriticalSection(device->state->device_queue_guard);
}
result = CallKernelDevice(device, ioctl_code, 0, 0);
if ( MM_FAILURE( result ) )
{
DPRINT1("Audio could not be reset\n");
return result;
}
if ( IsWaveDeviceType(device->type) )
{
if ( IsWaveInDeviceType(device->type) )
device->state->is_paused = TRUE;
else if ( IsWaveOutDeviceType(device->type) )
device->state->is_paused = FALSE;
/* TODO: Destroy completion thread + check ret val */
}
else /* MIDI input device */
{
/* TODO: Destroy completion thread + check ret val */
/* TODO - more stuff */
}
return result;
}
MMRESULT StopDeviceLooping(PWDMAUD_DEVICE_INFO device)

View file

@ -17,9 +17,15 @@
const char WDMAUD_DEVICE_INFO_SIG[4] = "WADI";
const char WDMAUD_DEVICE_STATE_SIG[4] = "WADS";
const char WDMAUD_NULL_SIGNATURE[4] = {0,0,0,0};
/*
IsValidDevicePath
Just checks to see if the string containing the path to the device path
(object) is a valid, readable string.
*/
BOOL IsValidDevicePath(WCHAR* path)
{
if (IsBadReadPtr(path, 1)) /* TODO: Replace with flags */
@ -33,43 +39,93 @@ BOOL IsValidDevicePath(WCHAR* path)
return TRUE;
}
MMRESULT ValidateDeviceInfo(PWDMAUD_DEVICE_INFO device_info)
{
if ( IsBadWritePtr(device_info, sizeof(WDMAUD_DEVICE_INFO)) )
return MMSYSERR_INVALPARAM;
/*
ValidateDeviceData
if ( *device_info->signature != *WDMAUD_DEVICE_INFO_SIG )
Checks that the memory pointed at by the device data pointer is writable,
and that it has a valid signature.
If the "state" member isn't NULL, the state structure is also validated
in the same way. If the "require_state" parameter is TRUE and the "state"
member is NULL, an error code is returned. Otherwise the "state" member
isn't validated and no error occurs.
*/
MMRESULT ValidateDeviceData(
PWDMAUD_DEVICE_INFO device,
BOOL require_state
)
{
if ( IsBadWritePtr(device, sizeof(WDMAUD_DEVICE_INFO)) )
{
DPRINT1("Device data structure not writable\n");
return MMSYSERR_INVALPARAM;
}
if ( strncmp(device->signature, WDMAUD_DEVICE_INFO_SIG, 4) != 0 )
{
DPRINT1("Device signature is invalid\n");
return MMSYSERR_INVALPARAM;
}
if ( ! IsValidDeviceType(device->type) )
{
DPRINT1("Invalid device type\n");
return MMSYSERR_INVALPARAM;
}
if ( device->id > 100 )
{
DPRINT1("Device ID is out of range\n");
return MMSYSERR_INVALPARAM;
}
/* Now we validate the device state (if present) */
if ( device->state )
{
if ( IsBadWritePtr(device->state, sizeof(WDMAUD_DEVICE_INFO)) )
{
DPRINT1("Device state structure not writable\n");
return MMSYSERR_INVALPARAM;
}
if ( strncmp(device->state->signature,
WDMAUD_DEVICE_STATE_SIG,
4) != 0 )
{
DPRINT1("Device state signature is invalid\n");
return MMSYSERR_INVALPARAM;
}
/* TODO: Validate state events */
}
else if ( require_state )
{
return MMSYSERR_INVALPARAM;
}
return MMSYSERR_NOERROR;
}
MMRESULT ValidateDeviceState(PWDMAUD_DEVICE_STATE state)
{
if ( IsBadWritePtr(state, sizeof(WDMAUD_DEVICE_INFO)) )
return MMSYSERR_INVALPARAM;
if ( *state->signature != *WDMAUD_DEVICE_STATE_SIG )
return MMSYSERR_INVALPARAM;
return MMSYSERR_NOERROR;
}
/*
ValidateDeviceStateEvents should be used in conjunction with the standard
state validation routine.
*/
state validation routine (NOT on its own!)
FIXME: The tests are wrong
*/
/*
MMRESULT ValidateDeviceStateEvents(PWDMAUD_DEVICE_STATE state)
{
if ( ( (DWORD) state->exit_thread_event == 0x00000000 ) &&
if ( ( (DWORD) state->exit_thread_event != 0x00000000 ) &&
( (DWORD) state->exit_thread_event != 0x48484848 ) )
{
DPRINT1("Bad exit thread event\n");
return MMSYSERR_INVALPARAM;
}
if ( ( (DWORD) state->queue_event == 0x00000000 ) &&
if ( ( (DWORD) state->queue_event != 0x00000000 ) &&
( (DWORD) state->queue_event != 0x42424242 ) &&
( (DWORD) state->queue_event != 0x43434343 ) )
{
@ -79,28 +135,32 @@ MMRESULT ValidateDeviceStateEvents(PWDMAUD_DEVICE_STATE state)
return MMSYSERR_NOERROR;
}
*/
MMRESULT ValidateDeviceInfoAndState(PWDMAUD_DEVICE_INFO device_info)
/*
CreateDeviceData
This is a glorified memory allocation routine, which acts as a primitive
constructor for a device data structure.
It validates the device path given, allocates memory for both the device
data and the device state data, copies the signatures over and sets the
device type accordingly.
In some cases, a state structure isn't required, so the creation of one can
be avoided by passing FALSE for the "with_state" parameter.
*/
PWDMAUD_DEVICE_INFO
CreateDeviceData(
CHAR device_type,
DWORD device_id,
WCHAR* device_path,
BOOL with_state
)
{
MMRESULT result;
result = ValidateDeviceInfo(device_info);
if ( result != MMSYSERR_NOERROR )
return result;
result = ValidateDeviceState(device_info->state);
if ( result != MMSYSERR_NOERROR )
return result;
return MMSYSERR_NOERROR;
}
PWDMAUD_DEVICE_INFO CreateDeviceData(CHAR device_type, WCHAR* device_path)
{
HANDLE heap = 0;
PWDMAUD_DEVICE_INFO device_data = 0;
BOOL success = FALSE;
PWDMAUD_DEVICE_INFO device = 0;
int path_size = 0;
DPRINT("Creating device data for device type %d\n", (int) device_type);
@ -115,121 +175,119 @@ PWDMAUD_DEVICE_INFO CreateDeviceData(CHAR device_type, WCHAR* device_path)
path_size = (lstrlen(device_path) + 1) * sizeof(WCHAR);
/* DPRINT("Size of path is %d\n", (int) path_size); */
heap = GetProcessHeap();
if ( ! heap )
{
DPRINT1("Couldn't get the process heap (error %d)\n",
(int) GetLastError());
goto cleanup;
}
DPRINT("Allocating %d bytes\n",
DPRINT("Allocating %d bytes for device data\n",
path_size + sizeof(WDMAUD_DEVICE_INFO));
/*
device_data = (PWDMAUD_DEVICE_INFO) HeapAlloc(heap,
HEAP_ZERO_MEMORY,
path_size + sizeof(WDMAUD_DEVICE_INFO));
*/
device_data = (PWDMAUD_DEVICE_INFO)
device = (PWDMAUD_DEVICE_INFO)
AllocMem(path_size + sizeof(WDMAUD_DEVICE_INFO));
if ( ! device_data )
if ( ! device )
{
DPRINT1("Unable to allocate memory for device data (error %d)\n",
(int) GetLastError());
goto cleanup;
}
DPRINT("Copying signature\n");
memcpy(device_data->signature, WDMAUD_DEVICE_INFO_SIG, 4);
/* Copy the signature and device path */
memcpy(device->signature, WDMAUD_DEVICE_INFO_SIG, 4);
lstrcpy(device->path, device_path);
DPRINT("Copying path (0x%x)\n", (int)device_path);
lstrcpy(device_data->path, device_path);
/* Initialize these common members */
device->id = device_id;
device->type = device_type;
device_data->type = device_type;
if ( with_state )
{
/* Allocate device state structure */
device->state = AllocMem(sizeof(WDMAUD_DEVICE_STATE));
if ( ! device->state )
{
DPRINT1("Couldn't allocate memory for device state (error %d)\n",
(int) GetLastError());
goto cleanup;
}
/* Copy the signature */
memcpy(device->state->signature, WDMAUD_DEVICE_STATE_SIG, 4);
}
success = TRUE;
cleanup :
{
/* No cleanup needed (no failures possible after allocation.) */
DPRINT("Performing cleanup\n");
if ( ! success )
{
if ( device )
{
if ( device->state )
{
ZeroMemory(device->state->signature, 4);
FreeMem(device->state);
}
return device_data;
ZeroMemory(device->signature, 4);
FreeMem(device);
}
}
return (success ? device : NULL);
}
}
/*
CloneDeviceData
DeleteDeviceData
This isn't all that great... Maybe some macros would be better:
BEGIN_CLONING_STRUCT(source, target)
CLONE_MEMBER(member)
END_CLONING_STRUCT()
Blanks out the device and device state structures, and frees the memory
associated with the structures.
The main problem is that sometimes we'll want to copy more than
the data presented here. I guess we could blindly copy EVERYTHING
but that'd be excessive.
TODO: Free critical sections / events if set?
*/
PWDMAUD_DEVICE_INFO CloneDeviceData(PWDMAUD_DEVICE_INFO original)
{
PWDMAUD_DEVICE_INFO clone = NULL;
if ( ValidateDeviceInfo(original) != MMSYSERR_NOERROR)
{
DPRINT1("Original device data was invalid\n");
return NULL;
}
/* This will set the type and path, so we can forget about those */
clone = CreateDeviceData(original->type, original->path);
if ( ! clone )
{
DPRINT1("Clone creation failed\n");
return NULL;
}
clone->id = original->id;
clone->wave_handle = original->wave_handle; /* ok? */
/* TODO: Maybe we should copy some more? */
return clone;
}
void DeleteDeviceData(PWDMAUD_DEVICE_INFO device_data)
{
HANDLE heap;
DPRINT("Deleting device data\n");
ASSERT( device_data );
/* Erase the signature to prevent any possible future mishaps */
*device_data->signature = *WDMAUD_NULL_SIGNATURE;
heap = GetProcessHeap();
if ( ! heap )
{
DPRINT1("Couldn't get the process heap (error %d)\n",
(int) GetLastError());
goto exit;
}
FreeMem(device_data);
/*
{
DPRINT1("Couldn't free device data memory (error %d)\n",
(int) GetLastError());
}
*/
exit :
/* We just return */
/* We don't really care if the structure is valid or not */
if ( ! device_data )
return;
if ( device_data->state )
{
/* We DON'T want these to be set - should we clean up? */
ASSERT ( ! device_data->state->device_queue_guard );
ASSERT ( ! device_data->state->queue_event );
ASSERT ( ! device_data->state->exit_thread_event );
/* Insert a cow (not sure if this is right or not) */
device_data->state->sample_size = 0xDEADBEEF;
/* Overwrite the structure with zeroes and free it */
ZeroMemory(device_data->state, sizeof(WDMAUD_DEVICE_STATE));
FreeMem(device_data->state);
}
/* Overwrite the structure with zeroes and free it */
ZeroMemory(device_data, sizeof(WDMAUD_DEVICE_INFO));
FreeMem(device_data);
}
/*
ModifyDevicePresence
Use this to add or remove devices in the kernel-mode driver. If the
"adding" parameter is TRUE, the device is added, otherwise it is removed.
"device_type" is WDMAUD_WAVE_IN, WDMAUD_WAVE_OUT, etc...
"device_path" specifies the NT object path of the device.
(I'm not sure what happens to devices that are added but never removed.)
*/
MMRESULT ModifyDevicePresence(
CHAR device_type,
WCHAR* device_path,
@ -249,7 +307,7 @@ MMRESULT ModifyDevicePresence(
ASSERT( IsValidDeviceType(device_type) );
ASSERT( device_path );
device_data = CreateDeviceData(device_type, device_path);
device_data = CreateDeviceData(device_type, 0, device_path, FALSE);
if ( ! device_data )
{
@ -292,6 +350,16 @@ MMRESULT ModifyDevicePresence(
}
}
/*
GetDeviceCount
Pretty straightforward - pass the device type (WDMAUD_WAVE_IN, ...) and
a topology device (NT object path) to obtain the number of devices
present in that topology of that particular type.
The topology path is supplied to us by winmm.
*/
DWORD GetDeviceCount(CHAR device_type, WCHAR* topology_path)
{
PWDMAUD_DEVICE_INFO device_data;
@ -299,7 +367,7 @@ DWORD GetDeviceCount(CHAR device_type, WCHAR* topology_path)
DPRINT("Topology path %S\n", topology_path);
device_data = CreateDeviceData(device_type, topology_path);
device_data = CreateDeviceData(device_type, 0, topology_path, FALSE);
if (! device_data)
{
@ -339,7 +407,8 @@ DWORD GetDeviceCount(CHAR device_type, WCHAR* topology_path)
This uses a different structure to the traditional documentation, because
we handle plug and play devices.
Much sleep was lost over implementing this.
Much sleep was lost over implementing this. I got the ID and type
parameters the wrong way round!
*/
MMRESULT GetDeviceCapabilities(
@ -363,7 +432,7 @@ MMRESULT GetDeviceCapabilities(
DPRINT("Going to have to query the kernel-mode part\n");
device = CreateDeviceData(device_type, device_path);
device = CreateDeviceData(device_type, device_id, device_path, FALSE);
if ( ! device )
{
@ -372,8 +441,9 @@ MMRESULT GetDeviceCapabilities(
goto cleanup;
}
device->id = device_id;
device->with_critical_section = FALSE;
/* These are not needed as they're already initialized */
ASSERT( device_id == device->id );
ASSERT( ! device->with_critical_section );
*(LPWORD)caps->pCaps = (WORD) 0x43;
@ -400,305 +470,224 @@ MMRESULT GetDeviceCapabilities(
}
}
MMRESULT TryOpenDevice(
/*
OpenDeviceViaKernel
Internal function to rub the kernel mode part of wdmaud the right way
so it opens a device on our behalf.
*/
MMRESULT
OpenDeviceViaKernel(
PWDMAUD_DEVICE_INFO device,
LPWAVEFORMATEX format
)
{
if ( device->id > 0x64 ) /* FIXME */
DWORD format_struct_len = 0;
DPRINT("Opening device via kernel\n");
if ( format->wFormatTag == 1 ) /* FIXME */
{
DPRINT1("device->id > 0x64 ! ???\n");
return MMSYSERR_BADDEVICEID; /* OK? */
}
/* Standard PCM format */
DWORD sample_size;
/* We'll only have a format set for wave devices */
if ( format )
{
if ( format->wFormatTag == 1 )
{
DWORD sample_size;
DPRINT("Standard (PCM) format\n");
DPRINT("Standard (PCM) format\n");
sample_size = format->nChannels * format->wBitsPerSample;
device->state->sample_size = sample_size;
sample_size = format->nChannels * format->wBitsPerSample;
if ( CallKernelDevice(device,
IOCTL_WDMAUD_OPEN_DEVICE,
0x10,
(DWORD)format)
!= MMSYSERR_NOERROR )
{
DPRINT("Call failed\n");
/* FIXME */
return MMSYSERR_NOTSUPPORTED; /* WAVERR_BADFORMAT? */
}
}
else
{
/* FIXME */
DPRINT("Non-PCM format\n");
return MMSYSERR_NOTSUPPORTED;
}
}
device->state->sample_size = sample_size;
/* If we got this far without error, the format is supported! */
return MMSYSERR_NOERROR;
}
MMRESULT OpenWaveDevice(
CHAR device_type,
DWORD device_id,
LPWAVEOPENDESC open_details,
DWORD flags,
DWORD user_data
)
{
HANDLE heap = 0;
PWDMAUD_DEVICE_INFO device = NULL;
WCHAR* device_path = NULL;
MMRESULT result = MMSYSERR_ERROR;
/* ASSERT(open_details); */
heap = GetProcessHeap();
if ( ! heap )
{
DPRINT1("Couldn't get the process heap (error %d)\n",
(int) GetLastError());
result = MMSYSERR_ERROR;
goto cleanup;
}
DPRINT("OpenDevice called\n");
device_path = (WCHAR*) open_details->dnDevNode;
device = CreateDeviceData(device_type, device_path);
if ( ! device )
{
DPRINT1("Couldn't create device data\n");
result = MMSYSERR_NOMEM;
goto cleanup;
}
DPRINT("Allocated device data, allocating device state\n");
device->state = HeapAlloc(heap,
HEAP_ZERO_MEMORY,
sizeof(WDMAUD_DEVICE_STATE));
if ( ! device->state )
{
DPRINT1("Couldn't allocate memory for device state (error %d)\n",
(int) GetLastError());
result = MMSYSERR_NOMEM;
goto cleanup;
}
/* FIXME: ok here ? */
device->type = device_type;
device->id = device_id;
device->flags = flags;
if ( flags & WAVE_FORMAT_QUERY )
{
DPRINT("Do I support this format? Hmm...\n");
result = TryOpenDevice(device, open_details->lpFormat);
if ( result != MMSYSERR_NOERROR )
{
DPRINT("Format not supported\n");
goto cleanup;
}
DPRINT("Yes, I do support this format!\n");
format_struct_len = 16; /* FIXME */
}
else
{
DPRINT("You actually want me to open the device, huh?\n");
/* Allocate memory for the "queue" critical section */
device->state->queue_critical_section =
HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(CRITICAL_SECTION));
if ( ! device->state->queue_critical_section )
{
DPRINT1("Couldn't allocate memory for queue critical section (error %d)\n",
(int) GetLastError());
result = MMSYSERR_NOMEM;
goto cleanup;
}
/* Initialize the critical section */
InitializeCriticalSection(device->state->queue_critical_section);
/* We need these so we can contact the client later */
device->client_instance = open_details->dwInstance;
device->client_callback = open_details->dwCallback;
/* Reset state */
device->state->open_descriptor = NULL;
device->state->unknown_24 = 0;
device->state->is_running = FALSE;
device->state->is_paused =
device->type == WDMAUD_WAVE_IN ? TRUE : FALSE;
memcpy(device->state->signature, WDMAUD_DEVICE_STATE_SIG, 4);
DPRINT("All systems are go...\n");
result = TryOpenDevice(device, open_details->lpFormat);
if ( result != MMSYSERR_NOERROR )
{
DPRINT1("Format not supported?\n");
goto cleanup; /* no need to set result - already done */
}
/* Enter the critical section while updating the device list */
EnterCriticalSection(device->state->queue_critical_section);
/* ... */
LeaveCriticalSection(device->state->queue_critical_section);
/* The wave device handle is actually our structure. Neat, eh? */
open_details->hWave = (HWAVE) device;
/* We also need to set our "user data" for winmm */
LPVOID* ud = (LPVOID*) user_data; /* FIXME */
*ud = device;
if (device->client_callback)
{
DWORD message;
message = (device->type == WDMAUD_WAVE_IN ? WIM_OPEN :
WDMAUD_WAVE_OUT ? WOM_OPEN : -1);
DPRINT("About to call the client callback\n");
/* Call the callback */
NotifyClient(device, message, 0, 0);
DPRINT("...it is done!\n");
}
result = MMSYSERR_NOERROR;
/* Non-standard format */
return MMSYSERR_NOTSUPPORTED; /* TODO */
}
/*
This cleanup may need checking for memory leakage. It's not very pretty
to look at, either...
*/
cleanup :
{
if ( ( result != MMSYSERR_NOERROR ) && ( heap ) )
{
if ( device )
{
if ( device->state )
{
if ( device->state->queue_critical_section )
{
DeleteCriticalSection(device->state->queue_critical_section);
HeapFree(heap, 0, device->state->queue_critical_section);
}
HeapFree(heap, 0, device->state);
}
DeleteDeviceData(device);
}
}
DPRINT("Returning %d\n", (int) result);
return result;
}
return CallKernelDevice(device,
IOCTL_WDMAUD_OPEN_DEVICE,
format_struct_len,
(DWORD)format);
}
MMRESULT CloseDevice(
PWDMAUD_DEVICE_INFO device
/* MOVEME */
LPCRITICAL_SECTION CreateCriticalSection()
{
LPCRITICAL_SECTION cs;
cs = AllocMem(sizeof(CRITICAL_SECTION));
if ( ! cs )
return NULL;
InitializeCriticalSection(cs);
return cs;
}
/*
OpenDevice
A generic "open device" function, which makes use of the above function
once parameters have been checked. This is capable of handling both
MIDI and wave devices, which is an improvement over the previous
implementation (which had a lot of duplicate functionality.)
*/
MMRESULT
OpenDevice(
CHAR device_type,
DWORD device_id,
LPVOID open_descriptor,
DWORD flags,
PWDMAUD_DEVICE_INFO* user_data
)
{
MMRESULT result = MMSYSERR_ERROR;
WCHAR* device_path;
PWDMAUD_DEVICE_INFO device;
LPWAVEFORMATEX format;
DPRINT("CloseDevice()\n");
/* As we support both types */
LPWAVEOPENDESC wave_opendesc = (LPWAVEOPENDESC) open_descriptor;
LPMIDIOPENDESC midi_opendesc = (LPMIDIOPENDESC) open_descriptor;
DUMP_WDMAUD_DEVICE_INFO(device);
/* FIXME: Does this just apply to wave, or MIDI also? */
if ( device_id > 100 )
return MMSYSERR_BADDEVICEID;
if ( ValidateDeviceInfo(device) != MMSYSERR_NOERROR )
/* Copy the appropriate dnDevNode value */
if ( IsWaveDeviceType(device_type) )
device_path = (WCHAR*) wave_opendesc->dnDevNode;
else if ( IsMidiDeviceType(device_type) )
device_path = (WCHAR*) midi_opendesc->dnDevNode;
else
return MMSYSERR_INVALPARAM;
device = CreateDeviceData(device_type, device_id, device_path, TRUE);
if ( ! device )
{
DPRINT1("Invalid device info passed to CloseDevice\n");
result = MMSYSERR_INVALHANDLE;
DPRINT1("Couldn't allocate memory for device data\n");
result = MMSYSERR_NOMEM;
goto cleanup;
}
/* TODO: Check state! */
device->flags = flags;
if ( device->id > 0x64 ) /* FIXME ? */
if ( ( IsWaveDeviceType(device->type) ) &&
( device->flags & WAVE_FORMAT_QUERY ) )
{
DPRINT1("??\n");
goto cleanup;
}
result = OpenDeviceViaKernel(device, wave_opendesc->lpFormat);
switch(device->type)
{
case WDMAUD_WAVE_OUT :
if ( result != MMSYSERR_NOERROR )
{
if ( device->state->open_descriptor )
{
DPRINT1("Device is still playing!\n");
result = WAVERR_STILLPLAYING;
goto cleanup;
}
/* TODO: Destroy completion thread */
break;
DPRINT1("Format not supported (mmsys error %d)\n", (int) result);
result = WAVERR_BADFORMAT;
}
default :
else
{
DPRINT1("Sorry, device type %d not supported yet!\n", (int) device->type);
DPRINT("Format supported\n");
result = MMSYSERR_NOERROR;
}
goto cleanup;
}
device->state->device_queue_guard = CreateCriticalSection();
if ( ! device->state->device_queue_guard )
{
DPRINT1("Couldn't create queue cs\n");
result = MMSYSERR_NOMEM;
goto cleanup;
}
/* Set up the callbacks */
device->client_instance = IsWaveDeviceType(device->type)
? wave_opendesc->dwInstance
: midi_opendesc->dwInstance;
device->client_callback = IsWaveDeviceType(device->type)
? wave_opendesc->dwCallback
: midi_opendesc->dwCallback;
/*
The device state will be stopped and unpaused already, but in some
cases this isn't the desired behaviour.
*/
/* FIXME: What do our friends MIDI in and out need? */
device->state->is_paused = IsWaveOutDeviceType(device->type) ? TRUE : FALSE;
if ( IsMidiOutDeviceType(device->type) )
{
device->state->midi_buffer = AllocMem(2048);
if ( ! device->state->midi_buffer )
{
DPRINT1("Couldn't allocate MIDI buffer\n");
result = MMSYSERR_NOMEM;
goto cleanup;
}
}
result = CallKernelDevice(device, IOCTL_WDMAUD_CLOSE_DEVICE, 0, 0);
/* Format is only for wave devices */
format = IsWaveDeviceType(device->type) ? wave_opendesc->lpFormat : NULL;
if ( result != MMSYSERR_NOERROR )
result = OpenDeviceViaKernel(device, format);
if ( MM_FAILURE(result) )
{
DPRINT1("Couldn't close the device!\n");
DPRINT1("FAILED to open device - mm error %d\n", (int) result);
goto cleanup;
}
if (device->client_callback)
EnterCriticalSection(device->state->device_queue_guard);
/* TODO */
LeaveCriticalSection(device->state->device_queue_guard);
if ( IsWaveDeviceType(device->type) )
wave_opendesc->hWave = (HWAVE) device;
else
midi_opendesc->hMidi = (HMIDI) device;
/* Our "user data" is actually the device information */
*user_data = device;
if ( device->client_callback )
{
DWORD message;
DWORD message = IsWaveInDeviceType(device->type) ? WIM_OPEN :
IsWaveOutDeviceType(device->type) ? WOM_OPEN :
IsMidiInDeviceType(device->type) ? MIM_OPEN :
MOM_OPEN;
message = (device->type == WDMAUD_WAVE_IN ? WIM_CLOSE :
WDMAUD_WAVE_OUT ? WOM_CLOSE :
WDMAUD_MIDI_IN ? MIM_CLOSE :
WDMAUD_MIDI_OUT ? MOM_CLOSE : -1);
DPRINT("About to call the client callback\n");
/* Call the callback */
DPRINT("Calling client with message %d\n", (int) message);
NotifyClient(device, message, 0, 0);
DPRINT("...it is done!\n");
}
/* Result was set earlier by CallKernelDevice */
result = MMSYSERR_NOERROR;
cleanup :
{
return result;
}
}

View file

@ -66,10 +66,9 @@ MMRESULT CallKernelDevice(
( ! IsAuxDeviceType(device->type) ) &&
( device->with_critical_section ) )
{
/* this seems to crash under some conditions */
ASSERT(device->state);
using_critical_section = TRUE;
EnterCriticalSection(device->state->queue_critical_section);
EnterCriticalSection(device->state->device_queue_guard);
}
DPRINT("Calling DeviceIoControl with IOCTL %x\n", (int) ioctl_code);
@ -110,7 +109,7 @@ MMRESULT CallKernelDevice(
{
/* Leave the critical section */
if ( using_critical_section )
LeaveCriticalSection(device->state->queue_critical_section);
LeaveCriticalSection(device->state->device_queue_guard);
if ( overlap.hEvent )
CloseHandle(overlap.hEvent);
@ -128,7 +127,7 @@ static BOOL ChangeKernelDeviceState(BOOL enable)
ioctl_code = enable ? IOCTL_WDMAUD_HELLO : IOCTL_WDMAUD_GOODBYE;
device = CreateDeviceData(WDMAUD_AUX, L"");
device = CreateDeviceData(WDMAUD_AUX, 0, L"", FALSE);
if ( ! device )
{
@ -136,9 +135,6 @@ static BOOL ChangeKernelDeviceState(BOOL enable)
return FALSE;
}
DPRINT("Setting device type and disabling critical section\n");
device->type = WDMAUD_AUX;
device->with_critical_section = FALSE;
DPRINT("Calling kernel device\n");
@ -318,33 +314,6 @@ BOOL EnableKernelInterface()
BOOL DisableKernelInterface()
{
return ChangeKernelDeviceState(FALSE);
#if 0 /* OLD CODE */
PWDMAUD_DEVICE_INFO device = NULL;
ASSERT(kernel_device_handle);
/* Say goodbyte to wdmaud.sys */
device = CreateDeviceData(WDMAUD_AUX, L"");
ASSERT(device);
DPRINT("Setting device type and disabling critical section\n");
device->type = WDMAUD_AUX;
device->with_critical_section = FALSE;
DPRINT("Calling kernel device\n");
ASSERT( CallKernelDevice(device, IOCTL_WDMAUD_GOODBYE, 0, 0) == MMSYSERR_NOERROR );
ASSERT( CloseHandle(kernel_device_handle) );
DPRINT("Kernel interface now disabled\n");
kernel_device_handle = NULL;
DeleteDeviceData(device);
#endif
}

View file

@ -58,7 +58,12 @@ VOID FreeMem(LPVOID pointer)
}
if ( ! HeapFree(heap, 0, pointer) )
alloc_count --;
{
DPRINT("Unable to free memory (error %d)\n", (int)GetLastError());
return;
}
alloc_count --;
ReportMem();
}

193
reactos/lib/wdmaud/midi.c Normal file
View file

@ -0,0 +1,193 @@
/*
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Multimedia
* FILE: lib/wdmaud/midi.c
* PURPOSE: WDM Audio Support - MIDI Device / Header Manipulation
* PROGRAMMER: Andrew Greenwood
* UPDATE HISTORY:
* Nov 29, 2005: Created
*/
#include <windows.h>
#include "wdmaud.h"
/*
OpenMidiDevice
OBSOLETE CODE - REFERENCE ONLY
*/
#if 0
MMRESULT
OpenMidiDevice(
CHAR device_type,
DWORD device_id,
LPMIDIOPENDESC open_details,
DWORD flags,
DWORD user_data
)
{
MMRESULT result = MMSYSERR_ERROR;
WCHAR* device_path;
PWDMAUD_DEVICE_INFO device;
ASSERT( open_details );
/* FIXME? Is this true for MIDI devs too then? */
if ( device_id > 100 )
return MMSYSERR_BADDEVICEID; /* Not sure about this */
/* TODO: Case statement for wave/midi selection? */
device_path = (WCHAR*) open_details->dnDevNode;
device = CreateDeviceData(device_type, device_id, device_path, TRUE);
if ( ! device )
{
DPRINT1("Couldn't create device data\n");
result = MMSYSERR_NOMEM;
goto cleanup;
}
device->type = device_type; /* not necessary */
device->id = device_id;
device->flags = flags;
/* Wave devices look for format query flag here... */
/* ... Validate Flags ? */
device->state->device_queue_guard = AllocMem(sizeof(CRITICAL_SECTION));
if ( ! device->state->device_queue_guard )
{
DPRINT1("Couldn't allocate memory for queue critical section (error %d)\n",
(int) GetLastError());
result = MMSYSERR_NOMEM;
goto cleanup;
}
/* Initialize the critical section */
InitializeCriticalSection(device->state->device_queue_guard);
/* We need these so we can contact the client later */
device->client_instance = open_details->dwInstance;
device->client_callback = open_details->dwCallback;
/* Reset state */
device->state->current_midi_header = NULL;
device->state->unknown_24 = 0;
device->state->is_running = FALSE;
device->state->is_paused = FALSE;
/* MIDI ONLY */
device->state->midi_buffer = 0;
device->state->running_status = 0x00;
/* For wave devices, we call the kernel NOW (but we're not handling wave) */
/* MIDI devices are a little more complicated... Code follows... */
/* MIDI OUT */
if ( device->type == WDMAUD_MIDI_OUT )
{
device->state->midi_buffer = AllocMem(2048);
if ( ! device->state->midi_buffer )
{
DPRINT1("Couldn't allocate memory for MIDI output buffer\n");
result = MMSYSERR_NOMEM;
goto cleanup;
}
}
/* Fairly generic code */
result = OpenDeviceViaKernel(device, NULL);
if ( result != MMSYSERR_NOERROR )
{
DPRINT1("Couldn't open device (mmsys error %d)\n", (int) result);
goto cleanup;
}
/* Enter the critical section while updating the device list */
EnterCriticalSection(device->state->device_queue_guard);
/* ... update MIDI list ... */
LeaveCriticalSection(device->state->device_queue_guard);
/* The MIDI device handle is actually our structure. Neat, eh? */
open_details->hMidi = (HMIDI) device;
/* We also need to set our "user data" for winmm */
LPVOID* ud = (LPVOID*) user_data; /* FIXME */
*ud = device;
/* MIDI specific code follows */
if ( device->type == WDMAUD_MIDI_IN )
{
/* TODO: Read MIDI data until none left? */
}
if ( device->client_callback )
{
DWORD message;
message = (device->type == WDMAUD_MIDI_IN ? MIM_OPEN : MOM_OPEN);
DPRINT("About to call the client callback\n");
/* Call the callback */
NotifyClient(device, message, 0, 0);
DPRINT("...it is done!\n");
}
result = MMSYSERR_NOERROR;
cleanup :
{
/* TODO!!!! */
return result;
}
}
#endif
MMRESULT
CloseMidiDevice(PWDMAUD_DEVICE_INFO device)
{
DPRINT("CloseMidiDevice\n");
return MMSYSERR_NOTSUPPORTED;
}
MMRESULT
WriteMidiShort(
PWDMAUD_DEVICE_INFO device,
DWORD message
)
{
DPRINT("WriteMidiShort\n");
return MMSYSERR_NOTSUPPORTED;
}
MMRESULT
WriteMidiBuffer(PWDMAUD_DEVICE_INFO device)
{
DPRINT("WriteMidiBuffer\n");
/* TODO - fix params too! */
return MMSYSERR_NOTSUPPORTED;
}
MMRESULT
ResetMidiDevice(PWDMAUD_DEVICE_INFO device)
{
DPRINT("ResetMidiDevice\n");
return MMSYSERR_NOTSUPPORTED;
}
/*
TODO:
SetVolume
GetVolume
SetPreferred
*/

View file

@ -21,11 +21,11 @@ DWORD WINAPI WaveCompletionThreadStart(LPVOID data)
DPRINT("WaveCompletionThread started\n");
EnterCriticalSection(device->state->queue_critical_section);
EnterCriticalSection(device->state->device_queue_guard);
while ( ! quit_loop )
{
result = ValidateDeviceInfoAndState(device);
result = ValidateDeviceData(device, TRUE);
if ( result != MMSYSERR_NOERROR )
{
@ -33,7 +33,8 @@ DWORD WINAPI WaveCompletionThreadStart(LPVOID data)
break;
}
result = ValidateDeviceStateEvents(device->state);
/* TODO: REIMPLEMENT */
/* result = ValidateDeviceStateEvents(device->state); */
if ( result != MMSYSERR_NOERROR )
{
@ -41,13 +42,13 @@ DWORD WINAPI WaveCompletionThreadStart(LPVOID data)
break;
}
if ( device->state->open_descriptor )
if ( device->state->current_wave_header )
{
DPRINT("No open descriptor found - running? %d\n", (int) device->state->is_running);
DPRINT("No current header - running? %d\n", (int) device->state->is_running);
if ( ! device->state->is_running )
{
LeaveCriticalSection(device->state->queue_critical_section);
LeaveCriticalSection(device->state->device_queue_guard);
DPRINT("Waiting for queue_event\n");
WaitForSingleObject(device->state->queue_event, INFINITE);
@ -76,7 +77,7 @@ DWORD WINAPI WaveCompletionThreadStart(LPVOID data)
}
else
{
PWAVEHDR wave_header = device->state->wave_header;
PWAVEHDR wave_header = device->state->current_wave_header;
DPRINT("An open descriptor or wave header was found\n");
@ -98,7 +99,7 @@ DWORD WINAPI WaveCompletionThreadStart(LPVOID data)
DPRINT("Activating the next header\n");
/* Activate the next header */
device->state->wave_header = wave_header->lpNext;
device->state->current_wave_header = wave_header->lpNext;
/* Reset this just in case */
prep_data = NULL;
@ -117,7 +118,7 @@ DWORD WINAPI WaveCompletionThreadStart(LPVOID data)
}
/* We do this here in case there's an error - deadlock = bad! */
LeaveCriticalSection(device->state->queue_critical_section);
LeaveCriticalSection(device->state->device_queue_guard);
if ( result != MMSYSERR_NOERROR)
goto cleanup;
@ -165,7 +166,7 @@ BOOL CreateCompletionThread(PWDMAUD_DEVICE_INFO device)
else
{
DPRINT("Thread is null\n");
if ( ( (DWORD) device->state->queue_event != 0 ) &&
( (DWORD) device->state->queue_event != MAGIC_42) &&
( (DWORD) device->state->queue_event != MAGIC_43) )
@ -229,7 +230,7 @@ BOOL CreateCompletionThread(PWDMAUD_DEVICE_INFO device)
DPRINT("Thread created! - %d\n", (int) device->state->thread);
/* TODO: Set priority */
}
}
return TRUE; /* TODO / FIXME */
}

View file

@ -23,7 +23,7 @@
#include "wdmaud.h"
APIENTRY LONG DriverProc(
APIENTRY LRESULT DriverProc(
DWORD DriverID,
HDRVR DriverHandle,
UINT Message,
@ -102,9 +102,11 @@ APIENTRY LONG DriverProc(
case DRV_INSTALL :
DPRINT("DRV_INSTALL\n");
return FALSE; /* ok? */
return TRUE; /* ok? */
/* DRV_REMOVE */
case DRV_REMOVE :
DPRINT("DRV_REMOVE\n");
return TRUE;
default :
DPRINT("?\n");
@ -123,7 +125,7 @@ void NotifyClient(
DriverCallback(device->client_callback,
HIWORD(device->flags),
(HDRVR) device->wave_handle,
(HDRVR) device->handle,
message,
device->client_instance,
0,
@ -199,8 +201,8 @@ APIENTRY DWORD wodMessage(
/*
* WODM_GETDEVCAPS
* Parameter 1 : Pointer to a WAVEOUTCAPS struct
* Parameter 2 : "Wave" device path? FIXME (NEW!)
* Parameter 1 : Pointer to a MDEVICECAPS struct
* Parameter 2 : Device path
*/
case WODM_GETDEVCAPS :
DPRINT("WODM_GETDEVCAPS\n");
@ -214,11 +216,14 @@ APIENTRY DWORD wodMessage(
*/
case WODM_OPEN :
DPRINT("WODM_OPEN\n");
return OpenWaveOut(id, (LPWAVEOPENDESC) p1, p2, user);
return OpenWaveOutDevice(id,
(LPWAVEOPENDESC) p1,
p2,
(PWDMAUD_DEVICE_INFO*) user);
case WODM_CLOSE :
DPRINT("WODM_CLOSE\n");
return CloseDevice((PWDMAUD_DEVICE_INFO) user); /* ugh! */
return CloseWaveDevice((PWDMAUD_DEVICE_INFO) user);
case WODM_PREPARE :
DPRINT("WODM_PREPARE\n");
@ -299,7 +304,46 @@ APIENTRY DWORD modMessage(
case MODM_GETDEVCAPS :
DPRINT("MODM_GETDEVCAPS\n");
return GetMidiOutCapabilities(id, (WCHAR*) p2, (LPMDEVICECAPSEX) p1);
};
case MODM_OPEN :
DPRINT("MODM_OPEN\n");
return OpenMidiOutDevice(id,
(LPMIDIOPENDESC) p1,
p2,
(PWDMAUD_DEVICE_INFO*) user);
case MODM_CLOSE :
DPRINT("MODM_CLOSE\n");
return CloseMidiDevice((PWDMAUD_DEVICE_INFO) user);
case MODM_DATA :
DPRINT("MODM_DATA\n");
return WriteMidiShort((PWDMAUD_DEVICE_INFO) user, p1);
case MODM_LONGDATA :
DPRINT("MODM_LONGDATA\n");
return MMSYSERR_NOTSUPPORTED;
case MODM_RESET :
DPRINT("MODM_RESET\n");
return ResetMidiDevice((PWDMAUD_DEVICE_INFO) user);
case MODM_SETVOLUME :
DPRINT("MODM_SETVOLUME\n");
return MMSYSERR_NOTSUPPORTED;
case MODM_GETVOLUME :
DPRINT("MODM_GETVOLUME\n");
return MMSYSERR_NOTSUPPORTED;
/* TODO: WINE's mmddk.h needs MODM_PREFERRED to be defined (value is ??) */
/*
case MODM_PREFERRED :
DPRINT("MODM_PREFERRED\n");
return MMSYSERR_NOTSUPPORTED;
*/
};
DPRINT("* NOT IMPLEMENTED *\n");
return MMSYSERR_NOTSUPPORTED;

View file

@ -2,8 +2,8 @@
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Multimedia
* FILE: lib/wdmaud/wavehdr.c
* PURPOSE: WDM Audio Support - Wave Header Manipulation
* FILE: lib/wdmaud/wave.c
* PURPOSE: WDM Audio Support - Wave Device / Header Manipulation
* PROGRAMMER: Andrew Greenwood
* UPDATE HISTORY:
* Nov 18, 2005: Created
@ -14,6 +14,194 @@
const char WAVE_PREPARE_DATA_SIG[4] = "WPPD";
/*
OBSOLETE CODE - FOR REFERENCE ONLY
*/
#if 0
MMRESULT OpenWaveDevice(
CHAR device_type,
DWORD device_id,
LPWAVEOPENDESC open_details,
DWORD flags,
DWORD user_data
)
{
MMRESULT result = MMSYSERR_ERROR;
WCHAR* device_path;
PWDMAUD_DEVICE_INFO device;
ASSERT( open_details );
ASSERT( open_details->lpFormat );
if ( device_id > 100 )
return MMSYSERR_BADDEVICEID; /* Not sure about this */
device_path = (WCHAR*) open_details->dnDevNode;
device = CreateDeviceData(device_type, device_id, device_path, TRUE);
if ( ! device )
{
DPRINT1("Couldn't create device data\n");
result = MMSYSERR_NOMEM;
goto cleanup;
}
device->type = device_type;
device->id = device_id;
device->flags = flags;
/* We don't deal with this here */
if ( flags & WAVE_FORMAT_QUERY )
{
result = QueryWaveFormatSupport(device, open_details);
DeleteDeviceData( device );
return result;
}
device->state->device_queue_guard = AllocMem(sizeof(CRITICAL_SECTION));
if ( ! device->state->device_queue_guard )
{
DPRINT1("Couldn't allocate memory for queue critical section (error %d)\n",
(int) GetLastError());
result = MMSYSERR_NOMEM;
goto cleanup;
}
/* Initialize the critical section */
InitializeCriticalSection(device->state->device_queue_guard);
/* We need these so we can contact the client later */
device->client_instance = open_details->dwInstance;
device->client_callback = open_details->dwCallback;
/* Reset state */
device->state->current_wave_header = NULL;
device->state->unknown_24 = 0;
device->state->is_running = FALSE;
device->state->is_paused =
device->type == WDMAUD_WAVE_IN ? TRUE : FALSE;
DPRINT("Opening the device\n");
result = OpenDeviceViaKernel(device, open_details->lpFormat);
if ( result != MMSYSERR_NOERROR )
{
DPRINT1("Couldn't open! mmsys error %d\n", (int) result);
/* TODO: WAVERR_BADFORMAT translation ? */
goto cleanup;
}
/* Enter the critical section while updating the device list */
EnterCriticalSection(device->state->device_queue_guard);
/* ... */
LeaveCriticalSection(device->state->device_queue_guard);
/* The wave device handle is actually our structure. Neat, eh? */
open_details->hWave = (HWAVE) device;
/* We also need to set our "user data" for winmm */
LPVOID* ud = (LPVOID*) user_data; /* FIXME */
*ud = device;
if ( device->client_callback )
{
DWORD message;
message = (device->type == WDMAUD_WAVE_IN ? WIM_OPEN : WOM_OPEN);
DPRINT("About to call the client callback\n");
/* Call the callback */
NotifyClient(device, message, 0, 0);
DPRINT("...it is done!\n");
}
result = MMSYSERR_NOERROR;
cleanup :
{
if ( result != MMSYSERR_NOERROR )
if ( device )
DeleteDeviceData(device);
return result;
}
}
#endif
MMRESULT CloseWaveDevice(
PWDMAUD_DEVICE_INFO device
)
{
MMRESULT result = MMSYSERR_ERROR;
result = ValidateDeviceData(device, TRUE);
if ( result != MMSYSERR_NOERROR )
{
DPRINT1("Device data invalid\n");
return MMSYSERR_INVALPARAM;
}
if ( ! IsWaveDeviceType(device->type) )
{
DPRINT1("Invalid device type (expected a WAVE device)\n");
return MMSYSERR_INVALPARAM;
}
/* TODO: Perform actual close */
if ( device->state->current_wave_header )
{
DPRINT1("Can't close! Device is still playing\n");
return WAVERR_STILLPLAYING;
}
/* TODO */
/* DestroyCompletionThread(device); - check result */
result = CallKernelDevice(device, IOCTL_WDMAUD_CLOSE_DEVICE, 0, 0);
if ( result != MMSYSERR_NOERROR )
{
DPRINT1("Close failed! mmsyserr %d\n", (int) result);
return result; /* TODO: convert? */
}
if ( device->client_callback )
{
DWORD message;
message = (device->type == WDMAUD_WAVE_IN ? WIM_CLOSE : WOM_CLOSE);
DPRINT("About to call the client callback\n");
/* Call the callback */
NotifyClient(device, message, 0, 0);
DPRINT("...it is done!\n");
}
/*
TODO:
Enter critical section
Loop through device list until we reach the end or until we find a
pointer matching "device".
Leave critical section
Delete critical section
...
*/
DeleteDeviceData(device);
return MMSYSERR_NOERROR;
}
/*
ValidateWaveHeaderPreparation Overview :
@ -28,22 +216,18 @@ const char WAVE_PREPARE_DATA_SIG[4] = "WPPD";
MMRESULT ValidateWavePreparationData(PWDMAUD_WAVE_PREPARATION_DATA prep_data)
{
/* UNIMPLEMENTED */
return MMSYSERR_NOERROR;
}
/*
ValidateWaveHeader Overview :
ValidateWaveHeader
Check that the pointer is valid to write to. If not, signal invalid
parameter.
Checks that the header memory can be written to, that the flags are
valid (using the mask 0xFFFFFFE0), and that the wave preparation data
is valid.
Perform a bitwise AND on the flags & 0xFFFFFFE0. If there are no bits
set, that's good. Otherwise give error and leave.
Check that the "reserved" member contains a valid WAVEPREPAREDATA
structure.
Return MMSYSERR_NOERROR if all's well!
Returns MMSYSERR_NOERROR if all's well, or MMSYSERR_INVALPARAM if not.
*/
MMRESULT ValidateWaveHeader(PWAVEHDR header)
@ -70,31 +254,16 @@ MMRESULT ValidateWaveHeader(PWAVEHDR header)
/*
PrepareWaveHeader Overview :
PrepareWaveHeader
Validate the parameters.
Checks the parameters are sane, allocates memory for a WAVEPREPAREDATA
structure and also memory for an OVERLAPPED structure.
Allocate and lock 12 bytes of global memory (fixed, zeroed) for the
WAVEPREPAREDATA structure.
After this, an un-named event is created (as hEvent of the OVERLAPPED)
structure, and the WAVEPREPAREDATA structure has its signature set
accordingly.
If that fails, return immediately.
Otherwise, allocate 20 bytes of memory on the process heap (with no
special flags.) This is for the overlapped member of the WAVEPREPAREDATA
structure.
If the HeapAlloc failed, free the global memory and return.
Create an event with all parameters false, NULL or zero. This is to be
stored as the hEvent member of the OVERLAPPED structure.
If the event creation failed, free all memory used and return.
If it succeeded, set the WAVEPREPAREDATA signature to "WPPD", and set
the reserved member of "header" to the pointer of the WAVEPREPAREDATA
instance.
Return MMSYSERR_NOTSUPPORTED so that winmm does further processing.
Returns MMSYSERR_NOTSUPPORTED so that winmm does further processing.
*/
MMRESULT PrepareWaveHeader(
@ -104,11 +273,11 @@ MMRESULT PrepareWaveHeader(
{
MMRESULT result = MMSYSERR_ERROR;
PWDMAUD_WAVE_PREPARATION_DATA prep_data = NULL;
HANDLE heap = NULL;
DPRINT("PrepareWaveHeader called\n");
result = ValidateDeviceInfoAndState(device);
/* Check the device data is valid */
result = ValidateDeviceData(device, TRUE);
if ( result != MMSYSERR_NOERROR )
{
@ -117,42 +286,21 @@ MMRESULT PrepareWaveHeader(
}
/* Make sure we were actually given a header to process */
DPRINT("Checking that a header was supplied\n");
if ( ! header )
{
DPRINT1("Bad header\n");
return MMSYSERR_INVALPARAM;
}
DPRINT("Checking flags\n");
/* I don't think Winmm would let this happen, but ya never know */
/*
ASSERT( ! header->dwFlags & WHDR_PREPARED );
ASSERT( header->dwFlags & WHDR_INQUEUE );
*/
/* NOTE: At this point, what happens if not prepared or already queued? */
header->lpNext = NULL;
header->reserved = 0;
heap = GetProcessHeap();
if ( ! heap )
{
DPRINT1("Couldn't obtain the process heap (error %d)\n",
(int)GetLastError());
result = MMSYSERR_NOMEM;
goto fail;
}
DPRINT("Allocating preparation data\n");
/* Allocate memory for the wave preparation data */
prep_data =
(PWDMAUD_WAVE_PREPARATION_DATA)
HeapAlloc(heap,
HEAP_ZERO_MEMORY,
sizeof(WDMAUD_WAVE_PREPARATION_DATA));
AllocMem(sizeof(WDMAUD_WAVE_PREPARATION_DATA));
if ( ! prep_data )
{
@ -162,20 +310,18 @@ MMRESULT PrepareWaveHeader(
goto fail;
}
DPRINT("Allocating overlapped data\n");
/* Create an event */
prep_data->overlapped = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(OVERLAPPED));
prep_data->overlapped = AllocMem(sizeof(OVERLAPPED));
if ( ! prep_data->overlapped )
{
DPRINT1("Couldn't allocate heap memory for overlapped structure (error %d)\n",
DPRINT1("Couldn't allocate memory for overlapped structure (error %d)\n",
(int)GetLastError());
result = MMSYSERR_NOMEM;
goto fail;
}
DPRINT("Creating overlapped event\n");
prep_data->overlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if ( ! prep_data->overlapped->hEvent )
@ -187,29 +333,26 @@ MMRESULT PrepareWaveHeader(
}
/* Copy the signature over and tie the prepare structure to the wave header */
DPRINT("Copying signature\n");
*prep_data->signature = *WAVE_PREPARE_DATA_SIG;
memcpy(prep_data->signature, WAVE_PREPARE_DATA_SIG, 4);
header->reserved = (DWORD) prep_data;
/* We return this so WINMM can do further processing */
result = MMSYSERR_NOTSUPPORTED;
return result;
fail :
{
if ( heap )
if ( prep_data )
{
if ( prep_data )
if ( prep_data->overlapped )
{
if ( prep_data->overlapped )
{
if ( prep_data->overlapped->hEvent )
CloseHandle(prep_data->overlapped->hEvent); /* ok? */
HeapFree(heap, 0, prep_data->overlapped);
}
if ( prep_data->overlapped->hEvent )
CloseHandle(prep_data->overlapped->hEvent); /* ok? */
HeapFree(heap, 0, prep_data);
FreeMem(prep_data->overlapped);
}
FreeMem(prep_data);
}
return result;
@ -219,13 +362,15 @@ MMRESULT PrepareWaveHeader(
/*
UnprepareWaveHeader
Cleans up after a header has been used, by killing the event we set up
above, and freeing the preparation data.
Winmm is intelligent enough to not call this function with a header that
is currently queued for playing!
*/
MMRESULT UnprepareWaveHeader(PWAVEHDR header)
{
HANDLE heap = NULL;
MMRESULT result = MMSYSERR_ERROR;
PWDMAUD_WAVE_PREPARATION_DATA prep_data = NULL;
@ -248,60 +393,23 @@ MMRESULT UnprepareWaveHeader(PWAVEHDR header)
return result;
}
/*
Overview :
Set "reserved" to NULL.
Close the event handle (overlapped->hEvent) and set that
structure member to NULL (not strictly necessary.)
Obtain the heap pointer and free the heap-allocated and global
memory allocated in PrepareWaveHeader.
*/
/* We're about to free the preparation structure, so this needs to go */
header->reserved = 0;
/* Kill the event */
CloseHandle(prep_data->overlapped->hEvent);
heap = GetProcessHeap();
if ( ! heap )
{
/* Not quite sure how we handle this */
DPRINT1("Couldn't obtain the process heap (error %d)\n",
(int)GetLastError());
result = MMSYSERR_ERROR;
goto cleanup;
}
if ( ! HeapFree(heap, 0, prep_data->overlapped) )
{
DPRINT1("Unable to free the OVERLAPPED memory (error %d)\n",
(int)GetLastError());
/* This shouldn't happen! */
ASSERT(FALSE);
}
FreeMem(prep_data->overlapped);
/* Overwrite the signature (structure will be invalid from now on) */
ZeroMemory(prep_data->signature, 4);
if ( ! HeapFree(heap, 0, prep_data) )
{
DPRINT1("Unable to free the preparation structure memory (error %d)\n",
(int)GetLastError());
/* This shouldn't happen! */
ASSERT(FALSE);
}
FreeMem(prep_data);
/* Always return like this so winmm thinks we didn't do anything */
DPRINT("Header now unprepared.\n");
result = MMSYSERR_NOTSUPPORTED;
cleanup :
return result;
return result;
}
/* Not sure about this */
@ -368,6 +476,13 @@ MMRESULT CompleteWaveHeader(PWAVEHDR header)
device info structure.
*/
/*
ValidateWriteWaveDataParams
This is just a helper function that shrinks WriteWaveData a little
bit.
*/
static MMRESULT ValidateWriteWaveDataParams(
PWDMAUD_DEVICE_INFO device,
PWAVEHDR header
@ -401,7 +516,7 @@ static MMRESULT ValidateWriteWaveDataParams(
return WAVERR_UNPREPARED;
}
result = ValidateDeviceInfoAndState(device);
result = ValidateDeviceData(device, TRUE);
if ( result != MMSYSERR_NOERROR )
{
@ -412,27 +527,41 @@ static MMRESULT ValidateWriteWaveDataParams(
return result;
}
/*
WriteWaveData
This is the exciting (?!) bit where playback actually begins. Various
validation takes place, before the header is queued for playback. Playback
can then begin. This entails telling the kernel-mode device about the
header, then telling the device to start playback.
It all seems pretty straightforward, but it's not all that easy...
*/
MMRESULT WriteWaveData(PWDMAUD_DEVICE_INFO device, PWAVEHDR header)
{
MMRESULT result = MMSYSERR_ERROR;
DWORD io_result = 0;
PWDMAUD_WAVE_PREPARATION_DATA prep_data = NULL;
PWDMAUD_DEVICE_INFO clone;
/* PWDMAUD_DEVICE_INFO clone; */
/* For the DeviceIoControl later */
DWORD ioctl_code;
DWORD bytes_returned;
DPRINT("SubmitWaveHeader called\n");
DPRINT("WriteWaveHeader called\n");
result = ValidateWriteWaveDataParams(device, header);
if ( result != MMSYSERR_NOERROR )
return result;
/*
TODO: Check to see if we actually get called with bad flags!
*/
/* Check to see if we actually get called with bad flags! */
if ( ! IS_WAVEHDR_FLAG_SET(header, WHDR_PREPARED) )
{
DPRINT1("Not prepared!\n");
return WAVERR_UNPREPARED;
}
/* Retrieve our precious data from the reserved member */
prep_data = (PWDMAUD_WAVE_PREPARATION_DATA) header->reserved;
@ -456,17 +585,17 @@ MMRESULT WriteWaveData(PWDMAUD_DEVICE_INFO device, PWAVEHDR header)
DPRINT("Flags == 0x%x\n", (int) header->dwFlags);
EnterCriticalSection(device->state->queue_critical_section);
EnterCriticalSection(device->state->device_queue_guard);
if ( ! device->state->wave_header )
if ( ! device->state->current_wave_header )
{
DPRINT("Device state wave_header is NULL\n");
device->state->wave_header = header;
device->state->current_wave_header = header;
/* My, what pretty symmetry you have... */
DPRINT("Queue event == %d\n", (int) device->state->queue_event);
DPRINT("Queue event == 0x%x\n", (int) device->state->queue_event);
if ( ( (DWORD) device->state->queue_event != 0 ) &&
( (DWORD) device->state->queue_event != MAGIC_42 ) &&
@ -483,40 +612,30 @@ MMRESULT WriteWaveData(PWDMAUD_DEVICE_INFO device, PWAVEHDR header)
ASSERT(FALSE);
}
LeaveCriticalSection(device->state->queue_critical_section);
LeaveCriticalSection(device->state->device_queue_guard);
/* CODE FROM SUBMITWAVEHEADER */
DPRINT("Now we actually perform header submission\n");
clone = CloneDeviceData(device);
if ( ! clone )
{
/* Safe to do this? */
DPRINT1("Clone creation failed\n");
return MMSYSERR_NOMEM;
}
/* Now we send the header to the kernel device */
if ( ! IsHeaderPrepared(header) )
{
DPRINT1("Unprepared header!\n");
DeleteDeviceData(clone);
return MMSYSERR_INVALPARAM;
result = MMSYSERR_INVALPARAM;
goto cleanup;
}
/* Not sure what this is for */
prep_data->offspring = clone;
/*
Not sure what this is for. I *think* it's used for tracking which
device a preparation belongs to.
*/
prep_data->offspring = device;
/* The modern version of WODM_WRITE, I guess ;) */
clone->ioctl_param1 = sizeof(WAVEHDR);
clone->ioctl_param2 = (DWORD) header;
device->ioctl_param1 = sizeof(WAVEHDR);
device->ioctl_param2 = (DWORD) header;
ioctl_code = clone->type == WDMAUD_WAVE_IN
? 0x1d8150 /* FIXME */
: IOCTL_WDMAUD_SUBMIT_WAVE_HDR;
DPRINT("Overlapped == 0x%x\n", (int) prep_data->overlapped);
ioctl_code = device->type == WDMAUD_WAVE_IN
? IOCTL_WDMAUD_SUBMIT_WAVE_IN_HDR /* FIXME */
: IOCTL_WDMAUD_SUBMIT_WAVE_OUT_HDR;
/*
FIXME:
@ -528,9 +647,9 @@ MMRESULT WriteWaveData(PWDMAUD_DEVICE_INFO device, PWAVEHDR header)
io_result =
DeviceIoControl(GetKernelInterface(),
ioctl_code,
clone,
sizeof(WDMAUD_DEVICE_INFO) + (lstrlen(clone->path) * 2),
clone,
device,
sizeof(WDMAUD_DEVICE_INFO) + (lstrlen(device->path) * 2),
device,
sizeof(WDMAUD_DEVICE_INFO),
&bytes_returned, /* ... */
prep_data->overlapped);
@ -542,28 +661,27 @@ MMRESULT WriteWaveData(PWDMAUD_DEVICE_INFO device, PWAVEHDR header)
DPRINT1("Wave header submission FAILED! (error %d)\n", (int) io_result);
CLEAR_WAVEHDR_FLAG(header, WHDR_INQUEUE);
device->state->queue_critical_section = NULL;
device->state->wave_header = NULL;
device->state->device_queue_guard = NULL;
device->state->current_wave_header = NULL;
return TranslateWinError(io_result);
}
/* CallKernelDevice(clone, ioctl_code, 0x20, (DWORD) header); */
DPRINT("Creating completion thread\n");
if ( ! CreateCompletionThread(device) )
{
DPRINT1("Couldn't create completion thread\n");
CLEAR_WAVEHDR_FLAG(header, WHDR_INQUEUE);
device->state->queue_critical_section = NULL;
device->state->wave_header = NULL;
device->state->device_queue_guard = NULL;
device->state->current_wave_header = NULL;
return MMSYSERR_ERROR; /* Care to be more specific? */
}
/* TODO */
/* ***** FIXME ****** THIS IS NASTY HACKERY ****** */
DPRINT("applying hacks\n");
@ -572,7 +690,7 @@ MMRESULT WriteWaveData(PWDMAUD_DEVICE_INFO device, PWAVEHDR header)
/* HACK */
DPRINT("%d\n", (int)
DeviceIoControl(GetKernelInterface(),
0x1d8104,
IOCTL_WDMAUD_WAVE_OUT_START,
device,
sizeof(WDMAUD_DEVICE_INFO) + (lstrlen(device->path) * 2),
device,
@ -597,5 +715,9 @@ MMRESULT WriteWaveData(PWDMAUD_DEVICE_INFO device, PWAVEHDR header)
result = MMSYSERR_NOERROR;
return result;
cleanup :
{
/* TODO */
return result;
}
}

View file

@ -39,6 +39,11 @@
#define REPORT_MM_RESULT(message, success) \
DPRINT("%s %s\n", message, success == MMSYSERR_NOERROR ? "succeeded" : "failed")
#define MM_SUCCESS(value) \
( value == MMSYSERR_NOERROR )
#define MM_FAILURE(value) \
( value != MMSYSERR_NOERROR )
#define GOBAL_CALLBACKS_PATH L"Global\\WDMAUD_Callbacks"
@ -65,55 +70,57 @@
#define IOCTL_WDMAUD_HELLO \
CTL_CODE(FILE_DEVICE_SOUND, 0x0000, METHOD_BUFFERED, FILE_WRITE_ACCESS)
#define IOCTL_WDMAUD_ADD_DEVICE 0x1d8004
#define IOCTL_WDMAUD_REMOVE_DEVICE 0x1d8008
#define IOCTL_WDMAUD_GET_CAPABILITIES 0x1d800c
#define IOCTL_WDMAUD_GET_DEVICE_COUNT 0x1d8010
#define IOCTL_WDMAUD_OPEN_DEVICE 0x1d8014
#define IOCTL_WDMAUD_CLOSE_DEVICE 0x1d8018
#define IOCTL_WDMAUD_AUX_GET_VOLUME 0x1d801c
#define IOCTL_WDMAUD_AUX_SET_VOLUME 0x1d8020
#define IOCTL_WDMAUD_ADD_DEVICE 0x1d8004
#define IOCTL_WDMAUD_REMOVE_DEVICE 0x1d8008
#define IOCTL_WDMAUD_GET_CAPABILITIES 0x1d800c
#define IOCTL_WDMAUD_GET_DEVICE_COUNT 0x1d8010
#define IOCTL_WDMAUD_OPEN_DEVICE 0x1d8014
#define IOCTL_WDMAUD_CLOSE_DEVICE 0x1d8018
#define IOCTL_WDMAUD_AUX_GET_VOLUME 0x1d801c
#define IOCTL_WDMAUD_AUX_SET_VOLUME 0x1d8020
/* 0x1d8024 */
#define IOCTL_WDMAUD_GOODBYE \
CTL_CODE(FILE_DEVICE_SOUND, 0x0009, METHOD_BUFFERED, FILE_WRITE_ACCESS)
#define IOCTL_WDMAUD_SET_PREFERRED 0x1d8028
#define IOCTL_WDMAUD_SET_PREFERRED 0x1d8028
#define IOCTL_WDMAUD_SET_STATE_UNKNOWN1 0x1d8100
#define IOCTL_WDMAUD_WAVE_OUT_START 0x1d8104 /* Reset wave in? */
#define IOCTL_WDMAUD_SET_STATE_UNKNOWN3 0x1d8108 /* Start wave in? */
#define IOCTL_WDMAUD_WAVE_OUT_STOP 0x1d8100
#define IOCTL_WDMAUD_WAVE_OUT_START 0x1d8104
#define IOCTL_WDMAUD_WAVE_OUT_RESET 0x1d8108
#define IOCTL_WDMAUD_BREAK_LOOP 0x1d810c
#define IOCTL_WDMAUD_GET_WAVE_OUT_POS 0x1d8110 /* Does something funky */
#define IOCTL_WDMAUD_SET_VOLUME 0x1d8114 /* Hasn't this already been covered? */
#define IOCTL_WDMAUD_UNKNOWN1 0x1d8018 /* Not used by wdmaud.drv */
#define IOCTL_WDMAUD_SUBMIT_WAVE_HDR 0x1d811c
#define IOCTL_WDMAUD_GET_WAVE_OUT_POS 0x1d8110 /* Does something funky */
#define IOCTL_WDMAUD_SET_VOLUME 0x1d8114 /* Already been covered? */
#define IOCTL_WDMAUD_UNKNOWN1 0x1d8118 /* Not used by wdmaud.drv */
#define IOCTL_WDMAUD_SUBMIT_WAVE_OUT_HDR 0x1d811c
#define IOCTL_WDMAUD_SET_STATE_UNKNOWN4 0x1d8140
#define IOCTL_WDMAUD_WAVE_IN_START 0x1d8144 /* Reset wave out? */
#define IOCTL_WDMAUD_SET_STATE_UNKNOWN6 0x1d8148 /* Start wave out? */
#define IOCTL_WDMAUD_WAVE_IN_STOP 0x1d8140
#define IOCTL_WDMAUD_WAVE_IN_START 0x1d8144
#define IOCTL_WDMAUD_WAVE_IN_RESET 0x1d8148
#define IOCTL_WDMAUD_SUBMIT_WAVE_IN_HDR 0x1d8150 /* FIXME: Unsure about this */
#define IOCTL_WDMAUD_MIDI_OUT_SHORT_MESSAGE \
0x1d8204 /* Wrong description? */
#define IOCTL_WDMAUD_UNKNOWN2 0x1d8208
#define IOCTL_WDMAUD_UNKNOWN2 0x1d8208
#define IOCTL_WDMAUD_MIDI_OUT_LONG_MESSAGE \
0x1d820c
#define IOCTL_WDMAUD_SUBMIT_MIDI_HDR 0x1d8210
#define IOCTL_WDMAUD_SUBMIT_MIDI_HDR 0x1d8210
#define IOCTL_WDMAUD_SET_STATE_UNKNOWN7 0x1d8240
#define IOCTL_WDMAUD_SET_STATE_UNKNOWN8 0x1d8244
#define IOCTL_WDMAUD_SET_STATE_UNKNOWN9 0x1d8248
#define IOCTL_WDMAUD_MIDI_IN_STOP 0x1d8240
#define IOCTL_WDMAUD_MIDI_IN_START 0x1d8244
#define IOCTL_WDMAUD_MIDI_IN_RESET 0x1d8248
#define IOCTL_WDMAUD_READ_MIDI_DATA 0x1d824c
#define IOCTL_WDMAUD_MIDI_MESSAGE 0x1d8300 /* Wrong description? */
#define IOCTL_WDMAUD_READ_MIDI_DATA 0x1d824c
#define IOCTL_WDMAUD_MIDI_MESSAGE 0x1d8300 /* Wrong description? */
#define IOCTL_WDMAUD_MIXER_UNKNOWN1 0x1d8310
#define IOCTL_WDMAUD_MIXER_UNKNOWN2 0x1d8314
#define IOCTL_WDMAUD_MIXER_UNKNOWN3 0x1d8318
#define IOCTL_WDMAUD_MIXER_UNKNOWN1 0x1d8310
#define IOCTL_WDMAUD_MIXER_UNKNOWN2 0x1d8314
#define IOCTL_WDMAUD_MIXER_UNKNOWN3 0x1d8318
/*
@ -180,6 +187,8 @@ typedef LPVOID PWDMAUD_HEADER;
There are also various "opendesc" structures, but these don't have any
common members. Regardless, this typedef simply serves as a placeholder
to indicate that to access the members, it should be cast accordingly.
TODO: Maybe have a generic OPEN routine that uses this?
*/
typedef struct OPENDESC *LPOPENDESC;
@ -188,41 +197,56 @@ typedef struct
DWORD sample_size;
HANDLE thread;
DWORD thread_id;
union
{
LPWAVEOPENDESC open_descriptor;
LPWAVEHDR wave_header;
LPWAVEHDR current_wave_header;
LPMIDIHDR current_midi_header;
};
DWORD unknown_10; /* pointer to something */
DWORD unknown_14;
LPCRITICAL_SECTION queue_critical_section;
LPCRITICAL_SECTION device_queue_guard;
HANDLE queue_event;
HANDLE exit_thread_event;
DWORD unknown_24;
DWORD is_paused;
DWORD is_running;
DWORD unknown_30;
DWORD unknown_34;
DWORD unknown_38;
LPVOID midi_buffer; /* for output ? */
DWORD running_status;
char signature[4];
} WDMAUD_DEVICE_STATE, *PWDMAUD_DEVICE_STATE;
typedef struct
{
DWORD unknown_00;
DWORD next_device;
DWORD id;
DWORD type;
HWAVE wave_handle;
HWAVE handle;
DWORD client_instance;
DWORD client_callback;
DWORD unknown_18;
DWORD flags;
DWORD ioctl_param2;
DWORD ioctl_param1;
DWORD with_critical_section;
DWORD string_2c;
DWORD unknown_30;
DWORD playing_notes;
DWORD unknown_38;
DWORD unknown_3c;
DWORD unknown_40;
@ -230,6 +254,7 @@ typedef struct
DWORD unknown_48;
DWORD unknown_4C;
DWORD unknown_50;
DWORD beef;
PWDMAUD_DEVICE_STATE state;
char signature[4];
@ -317,37 +342,63 @@ MMRESULT TranslateWinError(DWORD error);
/* user.c */
void NotifyClient(
void
NotifyClient(
PWDMAUD_DEVICE_INFO device,
DWORD message,
DWORD p1,
DWORD p2);
/* #define NotifyClient(device, message, p1, p2) ? */
DWORD p2
);
/* kernel.c */
BOOL EnableKernelInterface();
BOOL DisableKernelInterface();
HANDLE GetKernelInterface();
BOOL
EnableKernelInterface();
MMRESULT CallKernelDevice(
BOOL
DisableKernelInterface();
HANDLE
GetKernelInterface();
MMRESULT
CallKernelDevice(
PWDMAUD_DEVICE_INFO device,
DWORD ioctl_code,
DWORD param1,
DWORD param2);
/* devices.c */
BOOL IsValidDevicePath(WCHAR* path);
MMRESULT ValidateDeviceInfo(PWDMAUD_DEVICE_INFO info);
BOOL
IsValidDevicePath(WCHAR* path);
MMRESULT
ValidateDeviceData(
PWDMAUD_DEVICE_INFO device_data,
BOOL require_state
);
/*
MMRESULT ValidateDeviceState(PWDMAUD_DEVICE_STATE state);
MMRESULT ValidateDeviceStateEvents(PWDMAUD_DEVICE_STATE state);
MMRESULT ValidateDeviceInfoAndState(PWDMAUD_DEVICE_INFO device_info);
*/
/* TODO: Add ID parameter */
PWDMAUD_DEVICE_INFO
CreateDeviceData(
CHAR device_type,
DWORD device_id,
WCHAR* device_path,
BOOL with_state
);
void
DeleteDeviceData(PWDMAUD_DEVICE_INFO device_data);
PWDMAUD_DEVICE_INFO CreateDeviceData(CHAR device_type, WCHAR* device_path);
PWDMAUD_DEVICE_INFO CloneDeviceData(PWDMAUD_DEVICE_INFO original);
void DeleteDeviceData(PWDMAUD_DEVICE_INFO device_data);
/* mixer ... */
MMRESULT ModifyDevicePresence(
@ -388,7 +439,9 @@ MMRESULT ModifyDevicePresence(
RemoveDevice(WDMAUD_AUX, device_path)
DWORD GetDeviceCount(CHAR device_type, WCHAR* device_path);
DWORD
GetDeviceCount(CHAR device_type, WCHAR* device_path);
#define GetWaveInCount(device_path) GetDeviceCount(WDMAUD_WAVE_IN, device_path)
#define GetWaveOutCount(device_path) GetDeviceCount(WDMAUD_WAVE_OUT, device_path)
#define GetMidiInCount(device_path) GetDeviceCount(WDMAUD_MIDI_IN, device_path)
@ -396,11 +449,13 @@ DWORD GetDeviceCount(CHAR device_type, WCHAR* device_path);
#define GetMixerCount(device_path) GetDeviceCount(WDMAUD_MIXER, device_path)
#define GetAuxCount(device_path) GetDeviceCount(WDMAUD_AUX, device_path)
MMRESULT GetDeviceCapabilities(
MMRESULT
GetDeviceCapabilities(
CHAR device_type,
DWORD device_id,
WCHAR* device_path,
LPMDEVICECAPSEX caps);
LPMDEVICECAPSEX caps
);
#define GetWaveInCapabilities(id, device_path, caps) \
GetDeviceCapabilities(WDMAUD_WAVE_IN, id, device_path, caps);
@ -415,46 +470,115 @@ MMRESULT GetDeviceCapabilities(
#define GetAuxCapabilities(id, device_path, caps) \
GetDeviceCapabilities(WDMAUD_AUX, id, device_path, caps);
MMRESULT OpenWaveDevice(
MMRESULT
OpenDeviceViaKernel(
PWDMAUD_DEVICE_INFO device,
LPWAVEFORMATEX format
);
MMRESULT
OpenDevice(
CHAR device_type,
DWORD device_id,
LPVOID open_descriptor,
DWORD flags,
PWDMAUD_DEVICE_INFO* user_data
);
/* wave.c */
#if 0
MMRESULT
OpenWaveDevice(
CHAR device_type,
DWORD device_id,
LPWAVEOPENDESC open_details,
DWORD flags,
DWORD user_data);
DWORD user_data
);
#endif
#define OpenWaveOut(id, open_details, flags, user_data) \
OpenWaveDevice(WDMAUD_WAVE_OUT, id, open_details, flags, user_data);
#define OpenWaveInDevice(id, open_details, flags, user_data) \
OpenDevice(WDMAUD_WAVE_IN, id, open_details, flags, user_data);
#define OpenWaveOutDevice(id, open_details, flags, user_data) \
OpenDevice(WDMAUD_WAVE_OUT, id, open_details, flags, user_data);
#define OpenMidiInDevice(id, open_details, flags, user_data) \
OpenDevice(WDMAUD_MIDI_IN, id, open_details, flags, user_data);
#define OpenMidiOutDevice(id, open_details, flags, user_data) \
OpenDevice(WDMAUD_MIDI_OUT, id, open_details, flags, user_data);
MMRESULT CloseDevice(
MMRESULT
CloseWaveDevice(
PWDMAUD_DEVICE_INFO device
);
/* wavehdr.h */
#define SET_WAVEHDR_FLAG(header, flag) \
header->dwFlags |= flag
#define CLEAR_WAVEHDR_FLAG(header, flag) \
header->dwFlags &= ~flag
MMRESULT ValidateWavePreparationData(PWDMAUD_WAVE_PREPARATION_DATA prep_data);
#define IS_WAVEHDR_FLAG_SET(header, flag) \
( header->dwFlags & flag )
MMRESULT ValidateWaveHeader(PWAVEHDR header);
MMRESULT
ValidateWavePreparationData(PWDMAUD_WAVE_PREPARATION_DATA prep_data);
MMRESULT PrepareWaveHeader(
MMRESULT
ValidateWaveHeader(PWAVEHDR header);
MMRESULT
PrepareWaveHeader(
PWDMAUD_DEVICE_INFO device,
PWAVEHDR header
);
MMRESULT UnprepareWaveHeader(PWAVEHDR header);
MMRESULT
UnprepareWaveHeader(PWAVEHDR header);
#define IsHeaderPrepared(header) \
( header->reserved != 0 )
MMRESULT CompleteWaveHeader(PWAVEHDR header);
MMRESULT
CompleteWaveHeader(PWAVEHDR header);
MMRESULT
WriteWaveData(PWDMAUD_DEVICE_INFO device, PWAVEHDR header);
/* midi.c */
#if 0
MMRESULT
OpenMidiDevice(
CHAR device_type,
DWORD device_id,
LPMIDIOPENDESC open_details,
DWORD flags,
DWORD user_data
);
#endif
MMRESULT
CloseMidiDevice(
PWDMAUD_DEVICE_INFO device
);
MMRESULT
WriteMidiShort(
PWDMAUD_DEVICE_INFO device,
DWORD message
);
/* FIXME: Bad params */
MMRESULT
WriteMidiBuffer(PWDMAUD_DEVICE_INFO device);
MMRESULT
ResetMidiDevice(PWDMAUD_DEVICE_INFO device);
MMRESULT WriteWaveData(PWDMAUD_DEVICE_INFO device, PWAVEHDR header);
/* threads.c */

View file

@ -13,7 +13,8 @@
<file>user.c</file>
<file>kernel.c</file>
<file>devices.c</file>
<file>wavehdr.c</file>
<file>midi.c</file>
<file>wave.c</file>
<file>threads.c</file>
<file>helper.c</file>
<file>memtrack.c</file>