Fixed GETDEVCAPS message handling (bad macros) and implemented some primitive memory leak detection. Added: trunk/reactos/lib/wdmaud/callbacks.c Modified: trunk/reactos/lib/wdmaud/devices.c Added: trunk/reactos/lib/wdmaud/memtrack.c Modified: trunk/reactos/lib/wdmaud/user.c Modified: trunk/reactos/lib/wdmaud/wdmaud.def Modified: trunk/reactos/lib/wdmaud/wdmaud.h Modified: trunk/reactos/lib/wdmaud/wdmaud.xml _____
Added: trunk/reactos/lib/wdmaud/callbacks.c --- trunk/reactos/lib/wdmaud/callbacks.c 2005-11-25 03:41:24 UTC (rev 19539) +++ trunk/reactos/lib/wdmaud/callbacks.c 2005-11-25 03:50:49 UTC (rev 19540) @@ -0,0 +1,86 @@
+/* + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Multimedia + * FILE: lib/wdmaud/wdmaud.h + * PURPOSE: WDM Audio Support - Callbacks + * PROGRAMMER: Andrew Greenwood + * UPDATE HISTORY: + * Nov 24, 2005: Started + */ + +#include <windows.h> +#include "wdmaud.h" + + +/* + CheckCallbacks + + This appears to just be used by the mixer stuff. + + If the global callback file mapping handle isn't set, return FALSE. + + Check the first parameter. If the value is below that of the first + DWORD in the mapped view, return FALSE. + + TODO: Finish analysis +*/ + +BOOL CheckCallbacks() +{ + return FALSE; +} + +/* + CreateWdmaudCallbacks + + The original appears to use a security descriptor... We won't bother + with that for now. + + Create a file mapping with the name "Global\WDMAUD_Callbacks". + The current process is used as the file handle for the file mapping. The + file mapping attributes should be set to a length of 12 bytes, the + security descriptor we ignore and the handle doesn't need to be + inheritable. + + The maximum size should be set to 1028 bytes, and the file view + protection should be set to PAGE_READWRITE. + + The result of the file mapping creation is stored in the global callback + handle. + + If the creation succeeded, try and map a view of the file, from offset 0 + for 1028 bytes, with READ and WRITE access. + + If this succeeds, close the created file mapping handle, and set the + global handle to the one returned by the file mapping function. +*/ + +BOOL CreateWdmaudCallbacks() +{ + return FALSE; +} + + +/* + GetWdmaudCallbacks + + If the global callback handle is already set, do nothing. + + Open the file mapping to "Global\WDMAUD_Callbacks" with READ and WRITE + access. The handle doesn't need to be inherited. + + The handle is stored as the global callback handle. + + If the file was opened successfully, map the view of 1028 bytes from + offset 0 with READ and WRITE access. + + If this fails, close the global callback handle and set it to NULL. + + ...and then return +*/ + +BOOL GetWdmaudCallbacks() +{ + return FALSE; +} Property changes on: trunk/reactos/lib/wdmaud/callbacks.c ___________________________________________________________________ Name: svn:eol-style + native _____
Modified: trunk/reactos/lib/wdmaud/devices.c --- trunk/reactos/lib/wdmaud/devices.c 2005-11-25 03:41:24 UTC (rev 19539) +++ trunk/reactos/lib/wdmaud/devices.c 2005-11-25 03:50:49 UTC (rev 19540) @@ -17,6 +17,7 @@
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};
BOOL IsValidDevicePath(WCHAR* path) @@ -102,6 +103,8 @@ PWDMAUD_DEVICE_INFO device_data = 0; int path_size = 0;
+ DPRINT("Creating device data for device type %d\n", (int) device_type); + if ( ! IsValidDevicePath(device_path) ) { DPRINT1("No valid device interface given!\n"); @@ -110,7 +113,7 @@
/* Take into account this is a unicode string... */ path_size = (lstrlen(device_path) + 1) * sizeof(WCHAR); - DPRINT("Size of path is %d\n", (int) path_size); + /* DPRINT("Size of path is %d\n", (int) path_size); */
heap = GetProcessHeap();
@@ -123,11 +126,15 @@
DPRINT("Allocating %d bytes\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) + AllocMem(path_size + sizeof(WDMAUD_DEVICE_INFO)); + if ( ! device_data ) { DPRINT1("Unable to allocate memory for device data (error %d)\n", @@ -152,6 +159,19 @@ } }
+/* + CloneDeviceData + + This isn't all that great... Maybe some macros would be better: + BEGIN_CLONING_STRUCT(source, target) + CLONE_MEMBER(member) + END_CLONING_STRUCT() + + 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. +*/ + PWDMAUD_DEVICE_INFO CloneDeviceData(PWDMAUD_DEVICE_INFO original) { PWDMAUD_DEVICE_INFO clone = NULL; @@ -181,8 +201,33 @@
void DeleteDeviceData(PWDMAUD_DEVICE_INFO device_data) { + HANDLE heap; + ASSERT( device_data ); - /* TODO */ + + /* 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 */ + return; }
MMRESULT ModifyDevicePresence( @@ -198,11 +243,9 @@ DPRINT("ModifyDevicePresence - %s a device\n", adding ? "adding" : "removing");
- DPRINT("Topology path %S\n", device_path); + /* DPRINT("Topology path %S\n", device_path); */ + DPRINT("Devtype %d\n", (int) device_type);
- /* FIXME: DeviceType! */ - /* TODO: Assert on device type? */ - ASSERT( IsValidDeviceType(device_type) ); ASSERT( device_path );
@@ -215,8 +258,6 @@ goto cleanup; }
-/* device_data->type = device_type; */ - ioctl = adding ? IOCTL_WDMAUD_ADD_DEVICE : IOCTL_WDMAUD_REMOVE_DEVICE;
kernel_result = CallKernelDevice(device_data, @@ -268,7 +309,6 @@
DPRINT("Getting num devs\n");
- /*device_data->type = device_type;*/ device_data->with_critical_section = FALSE;
if ( CallKernelDevice(device_data, @@ -294,14 +334,19 @@ }
/* - This is a bit messed up + GetDeviceCapabilities + + This uses a different structure to the traditional documentation, because + we handle plug and play devices. + + Much sleep was lost over implementing this. */
MMRESULT GetDeviceCapabilities( CHAR device_type, DWORD device_id, WCHAR* device_path, - LPCOMMONCAPS caps + LPMDEVICECAPSEX caps ) { PWDMAUD_DEVICE_INFO device = NULL; @@ -309,39 +354,13 @@
DPRINT("Device path %S\n", device_path);
- /* Hmm - caps->wMid seems to be 0x54 (84) from XP's winmm.dll */ - if (caps->wMid == 0) + /* Is this right? */ + if (caps->cbSize == 0) { - return MMSYSERR_NOERROR; - - DPRINT("caps->wMid == 0\n"); - - DPRINT("Manufacturer: 0x%x (%d)\n", caps->wMid, caps->wMid); - DPRINT("Product: 0x%x (%d)\n", caps->wPid, caps->wPid); - DPRINT("Device is: %S\n", caps->szPname); - - if ( IsWaveOutDeviceType(device_type) ) - { - LPWAVEOUTCAPS woc = (LPWAVEOUTCAPS) caps; - DPRINT("Formats: %d\n", (int) woc->dwFormats); - DPRINT("Channels: %d\n", woc->wChannels); - DPRINT("Reserved: %d\n", woc->wReserved1); - DPRINT("Support: %d\n", (int) woc->dwSupport); - } - - return MMSYSERR_NOERROR; + DPRINT1("We appear to have been given an invalid parameter\n"); + return MMSYSERR_INVALPARAM; }
-#if 0 - int i; - for (i = 0; i < 64; i ++) - { - DPRINT("0x%x\n", *(((UCHAR*)caps) + i)); - } - - return MMSYSERR_NOERROR; -#endif - DPRINT("Going to have to query the kernel-mode part\n");
device = CreateDeviceData(device_type, device_path); @@ -356,23 +375,13 @@ device->id = device_id; device->with_critical_section = FALSE;
- /* ? */ - DPRINT("Caps wMid is 0x%x\n", (int) caps->wMid); - DPRINT("Driver version is 0x%x\n", (int) caps->vDriverVersion); + *(LPWORD)caps->pCaps = (WORD) 0x43;
- DPRINT("%S\n", (WCHAR*) caps->vDriverVersion); - - LPWORD theword; - theword = (LPWORD) caps->vDriverVersion; - *theword = 0x43; - //caps->vDriverVersion=0x4300; - - DPRINT("Calling kernel device\n"); result = CallKernelDevice(device, IOCTL_WDMAUD_GET_CAPABILITIES, - (DWORD)caps->wMid, - (DWORD)caps->vDriverVersion); + (DWORD)caps->cbSize, + (DWORD)caps->pCaps);
if ( result != MMSYSERR_NOERROR ) { @@ -380,11 +389,8 @@ goto cleanup; }
- + /* Return code will already be MMSYSERR_NOERROR by now */
- /* What do we return? */ - /*return MMSYSERR_NOERROR; */ /* already set by now */ - cleanup : { if ( device ) @@ -515,7 +521,7 @@ 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));
@@ -526,7 +532,7 @@ result = MMSYSERR_NOMEM; goto cleanup; } - + /* Initialize the critical section */
InitializeCriticalSection(device->state->queue_critical_section);
@@ -537,7 +543,7 @@ /* 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; @@ -547,7 +553,7 @@ DPRINT("All systems are go...\n");
result = TryOpenDevice(device, open_details->lpFormat); - + if ( result != MMSYSERR_NOERROR ) { DPRINT1("Format not supported?\n"); @@ -585,7 +591,8 @@ }
/* - This cleanup may need checking for memory leakage :/ + This cleanup may need checking for memory leakage. It's not very pretty + to look at, either... */
cleanup : @@ -601,7 +608,7 @@
DeleteCriticalSection(device->state->queue_critical_section); HeapFree(heap, 0, device->state->queue_critical_section); } - + HeapFree(heap, 0, device->state); }
_____
Added: trunk/reactos/lib/wdmaud/memtrack.c --- trunk/reactos/lib/wdmaud/memtrack.c 2005-11-25 03:41:24 UTC (rev 19539) +++ trunk/reactos/lib/wdmaud/memtrack.c 2005-11-25 03:50:49 UTC (rev 19540) @@ -0,0 +1,72 @@
+/* + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Multimedia + * FILE: lib/wdmaud/memtrack.c + * PURPOSE: WDM Audio Support - Memory Tracking + * PROGRAMMER: Andrew Greenwood + * UPDATE HISTORY: + * Nov 18, 2005: Created + * + */ + +#include "wdmaud.h" + +static int alloc_count = 0; + +LPVOID AllocMem(DWORD size) +{ + HANDLE heap; + LPVOID pointer; + + heap = GetProcessHeap(); + + if ( ! heap ) + { + DPRINT1("SEVERE ERROR! Couldn't get process heap! (error %d)\n", + (int) GetLastError()); + return NULL; + } + + pointer = HeapAlloc(heap, HEAP_ZERO_MEMORY, size); + + if ( pointer ) + alloc_count ++; + + ReportMem(); + + return pointer; +} + +VOID FreeMem(LPVOID pointer) +{ + HANDLE heap; + + if ( ! pointer ) + { + DPRINT1("Trying to free a NULL pointer!\n"); + return; + } + + heap = GetProcessHeap(); + + if ( ! heap ) + { + DPRINT1("SEVERE ERROR! Couldn't get process heap! (error %d)\n", + (int) GetLastError()); + return; + } + + if ( ! HeapFree(heap, 0, pointer) ) + alloc_count --; + + ReportMem(); +} + +VOID ReportMem() +{ + DPRINT("Memory blocks allocated: %d\n", (int) alloc_count); + + if ( alloc_count < 0 ) + DPRINT1("FREEMEM HAS BEEN CALLED TOO MANY TIMES!\n"); +} Property changes on: trunk/reactos/lib/wdmaud/memtrack.c ___________________________________________________________________ Name: svn:eol-style + native _____
Modified: trunk/reactos/lib/wdmaud/user.c --- trunk/reactos/lib/wdmaud/user.c 2005-11-25 03:41:24 UTC (rev 19539) +++ trunk/reactos/lib/wdmaud/user.c 2005-11-25 03:50:49 UTC (rev 19540) @@ -47,23 +47,24 @@
case DRV_LOAD : DPRINT("DRV_LOAD\n"); /* We should initialize the device list */ - return DRV_OK; // dont need to do any more + return TRUE; // dont need to do any more
case DRV_FREE : /* We should stop all wave and MIDI playback */ DPRINT("DRV_FREE\n"); - return DRV_OK; + return TRUE;
/* DRV_OPEN is sent when WINMM wishes to open the driver. Param1 can specify configuration information, but we don't need any. */ case DRV_OPEN : - return DRV_OK; + DPRINT("DRV_OPEN\n"); + return TRUE;
case DRV_CLOSE : DPRINT("DRV_CLOSE\n"); - return DRV_OK; + return TRUE;
/* Enabling this driver causes the kernel-mode portion of WDMAUD to @@ -84,7 +85,7 @@ case DRV_DISABLE : DPRINT("DRV_DISABLE\n"); DisableKernelInterface(); - return DRV_OK; + return TRUE;
/* We don't actually support configuration or installation, so these @@ -155,7 +156,7 @@ case WIDM_GETDEVCAPS : DPRINT("WIDM_GETDEVCAPS\n"); - return GetWaveInCapabilities(id, (WCHAR*) p2, (LPCOMMONCAPS) p1); + return GetWaveInCapabilities(id, (WCHAR*) p2, (LPMDEVICECAPSEX) p1); };
return MMSYSERR_NOERROR; @@ -185,7 +186,7 @@
case DRVM_EXIT : DPRINT("DRVM_EXIT\n"); - return RemoveWaveInDevice((WCHAR*) p2); /* FIXME? */ + return RemoveWaveOutDevice((WCHAR*) p2); /* FIXME? */
/* * WODM_GETNUMDEVS @@ -203,7 +204,7 @@ */ case WODM_GETDEVCAPS : DPRINT("WODM_GETDEVCAPS\n"); - return GetWaveOutCapabilities(id, (WCHAR*) p2, (LPCOMMONCAPS) p1); + return GetWaveOutCapabilities(id, (WCHAR*) p2, (LPMDEVICECAPSEX) p1);
/* * WODM_OPEN @@ -264,7 +265,7 @@
case MIDM_GETDEVCAPS : DPRINT("MIDM_GETDEVCAPS\n"); - return GetMidiInCapabilities(id, (WCHAR*) p2, (LPCOMMONCAPS) p1); + return GetMidiInCapabilities(id, (WCHAR*) p2, (LPMDEVICECAPSEX) p1); };
DPRINT("* NOT IMPLEMENTED *\n"); @@ -297,7 +298,7 @@
case MODM_GETDEVCAPS : DPRINT("MODM_GETDEVCAPS\n"); - return GetMidiOutCapabilities(id, (WCHAR*) p2, (LPCOMMONCAPS) p1); + return GetMidiOutCapabilities(id, (WCHAR*) p2, (LPMDEVICECAPSEX) p1); };
DPRINT("* NOT IMPLEMENTED *\n"); @@ -330,7 +331,7 @@
case MXDM_GETDEVCAPS : DPRINT("MXDM_GETDEVCAPS\n"); - return GetMixerCapabilities(id, (WCHAR*) p2, (LPCOMMONCAPS) p1); + return GetMixerCapabilities(id, (WCHAR*) p2, (LPMDEVICECAPSEX) p1);
/* ... */ }; @@ -359,7 +360,7 @@
case AUXDM_GETDEVCAPS : DPRINT("AUXDM_GETDEVCAPS\n"); - return GetAuxCapabilities(id, (WCHAR*) p2, (LPCOMMONCAPS) p1); + return GetAuxCapabilities(id, (WCHAR*) p2, (LPMDEVICECAPSEX) p1);
/* ... */ }; @@ -379,6 +380,8 @@
else if (Reason == DLL_PROCESS_DETACH) { + DPRINT("*** wdmaud.drv is being closed ***\n"); + ReportMem(); }
return TRUE; _____
Modified: trunk/reactos/lib/wdmaud/wdmaud.def --- trunk/reactos/lib/wdmaud/wdmaud.def 2005-11-25 03:41:24 UTC (rev 19539) +++ trunk/reactos/lib/wdmaud/wdmaud.def 2005-11-25 03:50:49 UTC (rev 19540) @@ -4,12 +4,23 @@
; ; ReactOS Operating System ; +; Each of the "message" functions can be commented out to stop the +; corresponding device type from being supported. This is mainly so we can +; focus on debugging a particular device type. +; LIBRARY wdmaud.drv EXPORTS +; DriverProc is needed so the driver can startup and shutdown DriverProc@20 +; Wave input support ;widMessage@20 +; Wave output support wodMessage@20 +; Midi input support ;midMessage@20 +; Midi output support ;modMessage@20 +; Mixer support ;mxdMessage@20 +; Auxiliary device support ;auxMessage@20 _____
Modified: trunk/reactos/lib/wdmaud/wdmaud.h --- trunk/reactos/lib/wdmaud/wdmaud.h 2005-11-25 03:41:24 UTC (rev 19539) +++ trunk/reactos/lib/wdmaud/wdmaud.h 2005-11-25 03:50:49 UTC (rev 19540) @@ -31,49 +31,7 @@
/* HACK! */ #define DbgPrint printf
-/* TODO: Put elsewhere */ -#if 0 -typedef struct tagWAVEOUTCAPS2A { - WORD wMid; /* manufacturer ID */ - WORD wPid; /* product ID */ - MMVERSION vDriverVersion; /* version of the driver */ - CHAR szPname[MAXPNAMELEN]; /* product name (NULL terminated string) */ - DWORD dwFormats; /* formats supported */ - WORD wChannels; /* number of sources supported */ - WORD wReserved1; /* packing */ - DWORD dwSupport; /* functionality supported by driver */ - GUID ManufacturerGuid; /* for extensible MID mapping */ - GUID ProductGuid; /* for extensible PID mapping */ - GUID NameGuid; /* for name lookup in registry */ -} WAVEOUTCAPS2A, *PWAVEOUTCAPS2A, *NPWAVEOUTCAPS2A, *LPWAVEOUTCAPS2A; -typedef struct tagWAVEOUTCAPS2W { - WORD wMid; /* manufacturer ID */ - WORD wPid; /* product ID */ - MMVERSION vDriverVersion; /* version of the driver */ - WCHAR szPname[MAXPNAMELEN]; /* product name (NULL terminated string) */ - DWORD dwFormats; /* formats supported */ - WORD wChannels; /* number of sources supported */ - WORD wReserved1; /* packing */ - DWORD dwSupport; /* functionality supported by driver */ - GUID ManufacturerGuid; /* for extensible MID mapping */ - GUID ProductGuid; /* for extensible PID mapping */ - GUID NameGuid; /* for name lookup in registry */ -} WAVEOUTCAPS2W, *PWAVEOUTCAPS2W, *NPWAVEOUTCAPS2W, *LPWAVEOUTCAPS2W; -#ifdef UNICODE -typedef WAVEOUTCAPS2W WAVEOUTCAPS2; -typedef PWAVEOUTCAPS2W PWAVEOUTCAPS2; -typedef NPWAVEOUTCAPS2W NPWAVEOUTCAPS2; -typedef LPWAVEOUTCAPS2W LPWAVEOUTCAPS2; -#else -typedef WAVEOUTCAPS2A WAVEOUTCAPS2; -typedef PWAVEOUTCAPS2A PWAVEOUTCAPS2; -typedef NPWAVEOUTCAPS2A NPWAVEOUTCAPS2; -typedef LPWAVEOUTCAPS2A LPWAVEOUTCAPS2; -#endif // UNICODE -#endif
- - /* Handy macros */ @@ -82,6 +40,10 @@ DPRINT("%s %s\n", message, success == MMSYSERR_NOERROR ? "succeeded" : "failed")
+ +#define GOBAL_CALLBACKS_PATH L"Global\WDMAUD_Callbacks" + + /* Private IOCTLs shared between wdmaud.sys and wdmaud.drv
@@ -201,19 +163,17 @@ of a device type. */
+/* + This is used as a general-purpose structure to retrieve capabilities + from the driver. +*/ typedef struct { - WORD wMid; - WORD wPid; - MMVERSION vDriverVersion; - WCHAR szPname[MAXPNAMELEN]; -} COMMONCAPSW, *LPCOMMONCAPSW; + DWORD cbSize; + LPVOID pCaps; +} MDEVICECAPSEX, *LPMDEVICECAPSEX;
-/* Unicode, anyone? */ -typedef COMMONCAPSW COMMONCAPS; -typedef LPCOMMONCAPSW LPCOMMONCAPS; - -/* More abstraction */ +/* Abstraction */ typedef LPVOID PWDMAUD_HEADER;
/* @@ -440,20 +400,20 @@ CHAR device_type, DWORD device_id, WCHAR* device_path, - LPCOMMONCAPS caps); + LPMDEVICECAPSEX caps);
#define GetWaveInCapabilities(id, device_path, caps) \ - GetDeviceCapabilities(id, WDMAUD_WAVE_IN, device_path, caps); + GetDeviceCapabilities(WDMAUD_WAVE_IN, id, device_path, caps); #define GetWaveOutCapabilities(id, device_path, caps) \ - GetDeviceCapabilities(id, WDMAUD_WAVE_OUT, device_path, caps); + GetDeviceCapabilities(WDMAUD_WAVE_OUT, id, device_path, caps); #define GetMidiInCapabilities(id, device_path, caps) \ - GetDeviceCapabilities(id, WDMAUD_MIDI_IN, device_path, caps); + GetDeviceCapabilities(WDMAUD_MIDI_IN, id, device_path, caps); #define GetMidiOutCapabilities(id, device_path, caps) \ - GetDeviceCapabilities(id, WDMAUD_MIDI_OUT, device_path, caps); + GetDeviceCapabilities(WDMAUD_MIDI_OUT, id, device_path, caps); #define GetMixerCapabilities(id, device_path, caps) \ - GetDeviceCapabilities(id, WDMAUD_MIXER, device_path, caps); + GetDeviceCapabilities(WDMAUD_MIXER, id, device_path, caps); #define GetAuxCapabilities(id, device_path, caps) \ - GetDeviceCapabilities(id, WDMAUD_AUX, device_path, caps); + GetDeviceCapabilities(WDMAUD_AUX, id, device_path, caps);
MMRESULT OpenWaveDevice( CHAR device_type, @@ -504,4 +464,15 @@
/* MORE... */
+/* + DEBUGGING + RESOURCE TRACKING +*/ + +LPVOID AllocMem(DWORD size); +VOID FreeMem(LPVOID pointer); +VOID ReportMem(); + +#define AutoAlloc(type) \ + (type*) MemAlloc(sizeof(type)); + #endif _____
Modified: trunk/reactos/lib/wdmaud/wdmaud.xml --- trunk/reactos/lib/wdmaud/wdmaud.xml 2005-11-25 03:41:24 UTC (rev 19539) +++ trunk/reactos/lib/wdmaud/wdmaud.xml 2005-11-25 03:50:49 UTC (rev 19540) @@ -16,5 +16,6 @@
<file>wavehdr.c</file> <file>threads.c</file> <file>helper.c</file> + <file>memtrack.c</file> <file>wdmaud.rc</file> </module>