Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

840 lines
26 KiB

/*
** Copyright (c) 1985-1998 Microsoft Corporation
**
** Title: mciwave.h - Multimedia Systems Media Control Interface
** streaming digital audio driver internal header file.
*/
/*
** Change log:
**
** DATE REV DESCRIPTION
** ----------- ----- ------------------------------------------
** 18-APR-1990 ROBWI Original
** 10-Jan-1992 MikeTri Ported to NT
** Aug 1994 Lauriegr Tried to add some explanation
*/
/********************* The OVERALL SCHEME OF THINGS ************************\
There are normally one or two files on the go. One is the original wave file,
the other is a temporary file. The data in these files is described by a
WAVEDESC which contains a pointer to an array of WAVEDATANODEs.
Each WAVEDATANODE identifies some part of one of the files.
The high order bit of the length field identifies which file (totally naff,
if you ask me, but I didn't invent it). Concatenate all the sections that
the WAVEDATANODEs identify and that's the wave data.
The WAVEDATANODEs actually form a linked list (linked by array indices not
pointers) and it is the concatenation of that list which defines the file.
There may also be some WAVEDATANODEs that define free space.
I'm confused about what exactly the dDataStart in the NODEs means. Is it the
position in the logical file or in one or other of the physical files? Either
way it probably gets messed up if you try deleting anything (KNOWN BUG).
LaurieGr
\***************************************************************************/
#ifndef MCIWAVE_H
#define MCIWAVE_H
#include <stdio.h>
#include <mmsystem.h>
#define WAIT_FOREVER ((DWORD)(-1))
#if DBG
#define PUBLIC extern /* Public label. SO DEBUGGER CAN */
#define PRIVATE extern /* Private label. SEE THE SYMBOLS */
#else
#define PUBLIC extern /* Public label. */
#define PRIVATE extern /* Private label. */
#endif
#define REALLYPRIVATE static
#define EXPORT /* Export function. */
#ifndef RC_INVOKED /* These are defined to RC */
#define STATICDT
#define STATICFN
#define STATIC
#endif /* RC_INVOKED */
/*
** This constant defines the maximum length of strings containing
** file paths. This number is the same as the string in OFSTRUCT.
*/
#define _MAX_PATH MAX_PATH
/*
** These two constants define extended commands that are use within the
** wave handler. The first can be sent to the MCI entry point, and the
** second is used entirely internally.
*/
#define MCI_MCIWAVE_PLAY_HOLD 0x01000000L
#define MCI_MCIWAVE_CUE 0x02000000L
/*
** The first two constants represent the maximum and minimum number of
** seconds of buffering that can be specified either on the SYSTEM.INI
** device driver entry, or in the MCI_OPEN command.
** The third constant defines the default number of seconds to use when
** calculating the number of seconds of buffering to allocate.
*/
#define MaxAudioSeconds 9
#define MinAudioSeconds 2
#define AudioSecondsDefault 4
/*
** This constant is used for recording when no record stopping point
** is specified.
*/
#define INFINITEFILESIZE 0X7FFFFFFFL
/*
** These constants represent the various RIFF components of the file.
*/
#define mmioWAVE mmioFOURCC('W','A','V','E')
#define mmioFMT mmioFOURCC('f','m','t',' ')
#define mmioDATA mmioFOURCC('d','a','t','a')
/*
** The following represent identifiers for string resources.
*/
#define IDS_PRODUCTNAME 0
#define IDS_MAPPER 1
#define IDS_COMMANDS 2
/*
** The following constant is used to specify the sample size when
** determing the input level during a Cued record. This number must
** be divisible by 4.
*/
#define NUM_LEVEL_SAMPLES 64L
/*
** The following constants represent specific task modes and task
** commands.
*/
#define MODE_PLAYING 0x0001
#define MODE_INSERT 0x0002
#define MODE_OVERWRITE 0x0004
#define MODE_PAUSED 0x0008
#define MODE_CUED 0x0010
#define MODE_HOLDING 0x0020
#define MODE_CLEANUP 0x0040
#define MODE_WAIT 0x0080
#define COMMAND_NEW 0x0100
#define COMMAND_PLAY 0x0200
#define COMMAND_INSERT 0x0400
#define COMMAND_OVERWRITE 0x0800
#define COMMAND_STOP 0x1000
#define COMMAND_CUE 0x2000
#define COMMAND_HOLD 0x4000
/*
** The following macros allow modes and commands to be added, removed,
** queried, set, and get.
*/
#define ADDMODE(pwd, m) ((pwd)->wMode |= (m))
#define REMOVEMODE(pwd, m) ((pwd)->wMode &= ~(m))
#define ISMODE(pwd, m) ((pwd)->wMode & (m))
#define SETMODE(pwd, m) ((pwd)->wMode = (m))
#define GETMODE(pwd) ((pwd)->wMode)
/*
** The following macros allow testing and setting of the current task
** state.
*/
#define ISTASKSTATE(pwd, s) ((pwd)->wTaskState == (s))
#define SETTASKSTATE(pwd, s) ((pwd)->wTaskState = (s))
#define TASKSTATE(pwd) ((pwd)->wTaskState)
/*
** Define message for state changes for device tasks
*/
#define WTM_STATECHANGE (WM_USER + 1)
/*
@doc INTERNAL MCIWAVE
@types DIRECTION |
The Direction enumeration is used internally in the MCI wave handler
to indicate the current direction of data flow. This is either input
(record), or output (play).
@flag input |
Indicates the direction is record.
@flag output |
Indicates the direction is playback.
@tagname tagDirection
*/
typedef enum tagDirection {
input,
output
} DIRECTION;
/*
** The following constants represent specific task states.
*/
#define TASKNONE 0
#define TASKINIT 1
#define TASKIDLE 2
#define TASKBUSY 3
#define TASKCLOSE 4
#define TASKSAVE 5
#define TASKDELETE 6
#define TASKCUT 7
/*
** The following constants and macros are used in dealing with data nodes,
** which are pointers to blocks of data. The first constant is used
** within these macros as a mask for block pointers which refer to data
** located in the temporary file, and not in the original read-only file.
*/
#define TEMPDATAMASK (0x80000000)
#define ENDOFNODES (-1)
#define ISTEMPDATA(lpwdn) (((lpwdn)->dDataStart & TEMPDATAMASK) != 0)
#define MASKDATASTART(d) ((d) | TEMPDATAMASK)
#define UNMASKDATASTART(lpwdn) ((lpwdn)->dDataStart & ~TEMPDATAMASK)
#define LPWDN(pwd, d) ((pwd)->lpWaveDataNode + (d))
#define RELEASEBLOCKNODE(lpwdn) ((lpwdn)->dDataLength = (DWORD)-1)
#define ISFREEBLOCKNODE(lpwdn) ((lpwdn)->dDataLength == (DWORD)-1)
/*
** The following constant is used to determine the allocation increments
** for data pointers
*/
#define DATANODEALLOCSIZE 32
/*
** The following macro is used to round a data offset to the next nearest
** buffer size increment.
*/
#define ROUNDDATA(pwd, d) ((((DWORD)(d) + (pwd)->dAudioBufferLen - 1) / (pwd)->dAudioBufferLen) * (pwd)->dAudioBufferLen)
#define BLOCKALIGN(pwd, d) ((((DWORD)(d) + (pwd)->pwavefmt->nBlockAlign - 1) / (pwd)->pwavefmt->nBlockAlign) * (pwd)->pwavefmt->nBlockAlign)
/************************************************************************/
/*
@doc INTERNAL MCIWAVE
@types WAVEDATANODE |
The Wave Data Node structure is used internally in the MCI wave
handler to store information about a block of wave data, located either
in the original file, or in the temporary data file. These structures
are used to form a linked list of wave data nodes that describe the
data in the entire file as it currently exists.
The headers themselves are allocated as an expandable array of global
memory, using <e>WAVEDATANODE.dDataLength<d> as an in-use flag when
searching the list for free entries to use. Note that a free entry
can also have free temporary data attached to it, as in the case of
performing a delete in which all the data for a specific node is
removed.
@field DWORD | dDataStart |
Indicates the absolute position at which the data for this node begins.
This element is also used in determining if the data pointed to by this
node is original data, or newly created temporary data. This is done
by masking the length with <f>TEMPDATAMASK<d>. The length can be
accessed by using <f>UNMASKDATASTART<d>.
@field DWORD | dDataLength |
Indicates the length of active data pointed to by the node. This
could be zero if say, a write failed. This contains -1 if the node
is not part of the linked list of active nodes.
@field DWORD | dTotalLength |
Indicates the actual total length of data available to this node. For
original data, this will always be the same as the
<e>WAVEDATANODE.dDataLength<d> element, but for temporary data, this
may be longer, as it is a block aligned number, the block lengths being
based on the size of wave data buffers. If the node is not in use, it
still may have data associated with it. If there is no data associated
with a free node, the total length is set to zero.
@field DWORD | dNextWaveDataNode |
This element is used for active nodes, and contains an index into the
array of nodes indicating the location of the next active node, or
<f>ENDOFNODES<d> to indicate the end of the list of active nodes.
@othertype WAVEDATANODE NEAR * | LPWAVEDATANODE |
A far pointer to the structure.
@tagname tagWaveDataNode
*/
typedef struct tagWaveDataNode {
DWORD dDataStart;
DWORD dDataLength;
DWORD dTotalLength;
DWORD dNextWaveDataNode;
} WAVEDATANODE,
FAR * LPWAVEDATANODE;
/*
@doc INTERNAL MCIWAVE
@types WAVEDESC |
The Wave Description structure is used internally in the MCI wave
handler to store details for each device, along with any state information.
@field MCIDEVICEID | wDeviceID |
MCI device identifier passed to the driver during driver open.
@field UINT | wMode |
Contains the current mode of the background task, if there is a task.
@flag MODE_PLAYING |
This mode is set when the task is actually doing playback. It is reset
before Cleanup mode is entered.
@flag MODE_INSERT |
This mode is set when the task is actually doing insert recording. It
is reset before Cleanup mode is entered.
@flag MODE_OVERWRITE |
This mode is set when the task is actually doing overwrite recording.
It is reset before Cleanup mode is entered.
@flag MODE_PAUSED |
This mode is set if playback or recording has been paused by an
MCI_PAUSE command.
@flag MODE_CUED |
This mode is entered when playback or recording has actually been cued.
@flag MODE_HOLDING |
This mode is entered when playback is about to block itself and hold
after doing playback.
@flag MODE_CLEANUP |
This mode is entered after playback or recording has finished, but
before the task has entered idle state, and new commands are being
ignored.
@flag MODE_WAIT |
This mode flag is used by both the calling task and the background
task. If the calling task received a Record or Play command with the
Wait flag, then this mode is set, so that if an error occurs during
playback or recording, the background task does not perform
notification, but just clears the notification callback handle. Just
before it performs notification, the background task clears this
flag so that the calling task will know that it should not return an
error condition. If the calling task is broken out of the wait loop,
it checks this flag to determine if it should report an error
condition.
@flag COMMAND_NEW |
This command specifies that a new command has been set.
@flag COMMAND_PLAY |
This command indicates that playback should be performed on the preset
parameters.
@flag COMMAND_INSERT |
This command indicates that insert recording should be performed on
the preset parameters.
@flag COMMAND_OVERWRITE |
This command indicates that overwrite recording should be performed on
the preset parameters.
@flag COMMAND_STOP |
This command indicates that playback or recording should stop.
@flag COMMAND_CUE |
This command indicates that playback should initially pause itself
before writing then enter Cue mode when all buffers have been written.
For recording, it should enter a level checking loop and wait for
further commands.
@flag COMMAND_HOLD |
This command specifies that playback should enter a hold state after
completing playback.
@field DWORD | dTimeFormat |
Indicates the current format of position values used in messages.
@flag MCI_FORMAT_MILLISECONDS |
Milliseconds.
@flag MCI_FORMAT_SAMPLES |
Samples.
@flag MCI_FORMAT_BYTES |
Bytes.
@field UINT | wSeconds |
Contains the desired amount of buffering in terms of seconds. This
is then converted to actual buffers, and limited by the predefined
min and max values.
@field HWND | hwndCallback |
If a message has specified notification, this contains the window
handle to where notification is to be sent. The handle is stored
here for delayed notification, and can be checked when the function
has finished or was interrupted.
@field HTASK | hTask |
If the MCI wave device instance was opened with an element attached,
this element contains the handle to the background task used for
playback and recording.
@field <t>DIRECTION<d> | Direction |
Indicates the current direction of data flow.
@flag input |
Indicates the direction is inwards, i.e. recording.
@flag output |
Indicates the direction is outwards, i.e. playback.
@field UINT | wTaskState |
MCIWAVE has a separate background task for every open instance of
mciwave. The task handle and task state are stored in the
per-instance data structure. The task can be in one of four states.
@flag TASKNONE |
This state is only set if the requested file cannot be opened during
task initialization. It is used so that the task create loop is able
to abort on an initialization failure.
@flag TASKINIT |
This is the initial task state set when the instance data structure is
initialized in <f>mwOpenDevice<d> before the actual task is created by
<f>mmTaskCreate<d>. After the task is created, <f>mwOpenDevice<d>
waits until the task state changes to TASKIDLE before returning success
so that the background task is definitely initialized after an open
call.
@flag TASKIDLE |
The task sets the state to TASKIDLE and blocks whenever there is
nothing to do. When the task wakes, the state is either TASKCLOSE if
the instance is being closed or else TASKBUSY if the task is to begin
recording or playback of the file.
@flag TASKCLOSE |
<f>mwCloseDevice<d> stops playback or recording which forces the task
state to TASKIDLE and then sets the state to TASKCLOSE and wakes the
task so that the task will destroy itself.
@flag TASKBUSY |
The task is in this state during playback and recording.
@flag TASKCLOSE |
The task is closing and about to terminate.
@flag TASKSAVE |
The task saving the current data to the specified file.
@flag TASKDELETE |
The task deleting the specified data.
@flag TASKCUT |
The task cutting the specified data (Not implemented).
@field UINT | idOut |
Wave device id of output device to use, or WAVE_MAPPER for any.
@field UINT | idIn |
Wave device id of input device to use, or WAVE_MAPPER for any.
@field HWAVEOUT | hWaveOut |
Output wave device handle when in use.
@field HWAVEIN | hWaveIn |
Input wave device handle when in use.
@field <t>LPWAVEHDR<d> | rglpWaveHdr |
Pointer to array of audio buffers for wave buffering.
@field DWORD | dCur |
Current position in file in bytes.
@field DWORD | dFrom |
Position in bytes at which playback or recording should begin.
@field DWORD | dTo |
Position in bytes at which playback or recording should terminate,
or <f>INFINITEFILESIZE<d> if recording should continue until stopped.
@field DWORD | dSize |
Actual wave data size in bytes.
@field char | aszFile |
Contains the name of the element attached to the MCI wave device
instance, if any. This might be a zero length string if the file is
new and has not been named yet.
@field char | aszTempFile |
Contains the name of the temporary data file, if any.
@field <t>HMMIO<d> | hmmio |
MMIO identifier of the element attached to the MCI wave device
instance, if any.
@field HFILE | hfTempBuffers |
Contains the temporary data DOS file handle, if any, else HFILE_ERROR.
@field <t>LPMMIOPROC<d> | pIOProc |
Contains a pointer to the alternate MMIO IO procedure, if any, else
NULL.
@field <t>LPWAVEDATANODE<d> | lpWaveDataNode |
Points to the array of wave data nodes. This is allocated when the
file opens, so it is always valid. The array is expanded as needed.
@field DWORD | dRiffData |
This contains an offset into the original file, if any, indicating
the actual starting point of the wave data, which in a RIFF file will
not be zero.
@field DWORD | dWaveDataStartNode |
This contains an index to the first active data pointer in the linked
list of data pointer nodes.
@field DWORD | dWaveDataCurrentNode |
This contains an index to the current active data pointer in the
linked list of data pointer nodes.
@field DWORD | dVirtualWaveDataStart |
This contains a virtual starting point representing logically where in
the file the data for the current node begins.
@field DWORD | dWaveDataNodes |
This indicates the total number of data pointer nodes available.
@field DWORD | dWaveTempDataLength |
This contains the current length of the temporary data file, if any.
@field DWORD | dLevel |
Current input level if it is being scanned.
@field UINT | wTaskError |
Task error return.
@field UINT | wAudioBuffers |
Number of audio buffers actually allocated during playback or recording.
@field DWORD | wAudioBufferLen |
Length of each audio buffer.
@field PSTR | szSaveFile |
During a save command, this optionally contains the name of the file
to save to, unless data is being saved to the original file.
@field UINT | wFormatSize |
This contains the size of the wave header, which is used when saving
data to a new file.
@field <t>WAVEFORMAT<d> | pwavefmt |
Pointer to the wave format header.
@field HANDLE | hTaskHandle |
Handle to the thread that started this request
@field CRITCAL_SECTION | CritSec |
Serialisation object for threads accessing this <t>WAVEDESC<d> structure
@othertype WAVEDESC NEAR * | PWAVEDESC |
A near pointer to the structure.
@tagname tagWaveDesc
*/
#ifndef MMNOMMIO
#ifndef MMNOWAVE
typedef struct tagWaveDesc {
MCIDEVICEID wDeviceID;
UINT wMode;
DWORD dTimeFormat;
UINT wSeconds;
HWND hwndCallback;
DWORD hTask;
//HANDLE hTask;
DIRECTION Direction;
UINT wTaskState;
UINT idOut;
UINT idIn;
HWAVEOUT hWaveOut;
HWAVEIN hWaveIn;
DWORD dCur;
DWORD dFrom;
DWORD dTo;
DWORD dSize;
HMMIO hmmio;
HANDLE hTempBuffers;
LPMMIOPROC pIOProc;
LPWAVEDATANODE lpWaveDataNode;
DWORD dRiffData;
DWORD dWaveDataStartNode;
DWORD dWaveDataCurrentNode;
DWORD dVirtualWaveDataStart;
DWORD dWaveDataNodes;
DWORD dWaveTempDataLength;
DWORD dLevel;
UINT wTaskError;
UINT wAudioBuffers;
DWORD dAudioBufferLen;
LPWSTR szSaveFile;
UINT wFormatSize;
WAVEFORMAT NEAR * pwavefmt;
HANDLE hTaskHandle; // Handle of the thread running this job
LPWAVEHDR rglpWaveHdr[MaxAudioSeconds];
WCHAR aszFile[_MAX_PATH];
WCHAR aszTempFile[_MAX_PATH];
} WAVEDESC;
typedef WAVEDESC * PWAVEDESC;
/************************************************************************/
//PRIVATE DWORD PASCAL FAR time2bytes(
// PWAVEDESC pwd,
// DWORD dTime,
// DWORD dFormat);
//
//PRIVATE DWORD PASCAL FAR bytes2time(
// PWAVEDESC pwd,
// DWORD dBytes,
// DWORD dFormat);
PUBLIC VOID PASCAL FAR mwDelayedNotify(
PWAVEDESC pwd,
UINT uStatus);
PUBLIC LPWAVEHDR * PASCAL FAR NextWaveHdr(
PWAVEDESC pwd,
LPWAVEHDR * lplpWaveHdr);
PUBLIC UINT PASCAL FAR PlayFile(
register PWAVEDESC pwd);
PUBLIC UINT PASCAL FAR RecordFile(
register PWAVEDESC pwd);
PUBLIC DWORD PASCAL FAR mwInfo(
PWAVEDESC pwd,
DWORD dFlags,
LPMCI_INFO_PARMS lpInfo);
PUBLIC DWORD PASCAL FAR mwGetDevCaps(
PWAVEDESC pwd,
DWORD dFlags,
LPMCI_GETDEVCAPS_PARMS lpCaps);
PUBLIC DWORD PASCAL FAR mwAllocMoreBlockNodes(
PWAVEDESC pwd);
PUBLIC DWORD PASCAL FAR mwFindAnyFreeDataNode(
PWAVEDESC pwd,
DWORD dMinDataLength);
PUBLIC VOID PASCAL FAR mwDeleteData(
PWAVEDESC pwd);
PUBLIC VOID PASCAL FAR mwSaveData(
PWAVEDESC pwd);
PUBLIC VOID PASCAL FAR InitMMIOOpen(
PWAVEDESC pwd,
LPMMIOINFO lpmmioInfo);
#endif // MMNOWAVE
#endif // MMNOMMIO
PUBLIC LRESULT PASCAL FAR mciDriverEntry(
MCIDEVICEID wDeviceID,
UINT uMessage,
DWORD dFlags,
LPMCI_GENERIC_PARMS lpParms);
PUBLIC INT_PTR PASCAL FAR Config(
HWND hWnd,
LPDRVCONFIGINFO lpdci,
HINSTANCE hInstance);
PUBLIC UINT PASCAL FAR GetAudioSeconds(
LPCWSTR pch);
__inline BOOL MySeekFile(HANDLE hFile, LONG Position)
{
return 0xFFFFFFFF != SetFilePointer(hFile, Position, NULL, FILE_BEGIN);
}
__inline BOOL MyReadFile(HANDLE hFile, LPVOID pBuffer, ULONG cBytesToRead, PULONG pcBytesRead)
{
BOOL fReturn;
ULONG cBytesRead;
if (!pcBytesRead) pcBytesRead = &cBytesRead;
fReturn = ReadFile(hFile, pBuffer, cBytesToRead, pcBytesRead, NULL);
if (fReturn && (*pcBytesRead == cBytesToRead))
{
return TRUE;
}
*pcBytesRead = -1;
return FALSE;
}
__inline BOOL MyWriteFile(HANDLE hFile, LPCVOID pBuffer, ULONG cBytesToWrite, PULONG pcBytesWritten)
{
BOOL fReturn;
ULONG cBytesWritten;
if (!pcBytesWritten) pcBytesWritten = &cBytesWritten;
fReturn = WriteFile(hFile, pBuffer, cBytesToWrite, pcBytesWritten, NULL);
if (fReturn && (*pcBytesWritten == cBytesToWrite))
{
return TRUE;
}
*pcBytesWritten = -1;
return FALSE;
}
/************************************************************************/
/*
** This defines a stack and code based pointer types.
*/
//#define STACK _based(_segname("_STACK"))
//#define SZCODE char _based(_segname("_CODE"))
//typedef char STACK * SSZ;
#define SZCODE WCHAR // Should be sufficient,
typedef WCHAR *SSZ; // as segments no longer matter in Win32
/************************************************************************/
PUBLIC HINSTANCE hModuleInstance;
PUBLIC UINT cWaveOutMax;
PUBLIC UINT cWaveInMax;
PUBLIC UINT wAudioSeconds;
/***************************************************************************
Synchronisation support
***************************************************************************/
VOID InitCrit(VOID);
VOID DeleteCrit(VOID);
#if DBG
extern VOID DbgEnterCrit(UINT ln, LPCSTR lpszFile);
#define EnterCrit() DbgEnterCrit(__LINE__, __FILE__)
#else
VOID EnterCrit(VOID);
#endif
VOID LeaveCrit(VOID);
VOID TaskWaitComplete(HANDLE h);
UINT TaskBlock(VOID);
BOOL TaskSignal(DWORD h, UINT Msg);
#ifndef MMNOMMIO
#ifndef MMNOWAVE
#if DBG
extern DWORD dwCritSecOwner;
#define mmYield(pwd) mmDbgYield(pwd, __LINE__, __FILE__)
extern VOID mmDbgYield(PWAVEDESC pwd, UINT ln, LPCSTR lpszFile);
#define CheckIn() WinAssert((GetCurrentThreadId() == dwCritSecOwner))
#define CheckOut() WinAssert((GetCurrentThreadId() != dwCritSecOwner))
#else
#define CheckIn()
#define CheckOut()
#define mmYield(pwd) \
{ \
LeaveCrit(); \
Sleep(10); \
EnterCrit(); \
}
#endif
#endif
#endif
/***************************************************************************
DEBUGGING SUPPORT
***************************************************************************/
#if DBG
extern void mciwaveDbgOut(LPSTR lpszFormat, ...);
extern void mciwaveInitDebugLevel(void);
extern void dDbgAssert(LPSTR exp, LPSTR file, int line);
#define WinAssert(exp) \
((exp) ? (void)0 : dDbgAssert(#exp, __FILE__, __LINE__))
#define WinEval(exp) \
((__dwEval=(DWORD)(exp)), \
__dwEval ? (void)0 : dDbgAssert(#exp, __FILE__, __LINE__), __dwEval)
int mciwaveDebugLevel;
#define dprintf( _x_ ) mciwaveDbgOut _x_
#define dprintf1( _x_ ) if (mciwaveDebugLevel >= 1) mciwaveDbgOut _x_
#define dprintf2( _x_ ) if (mciwaveDebugLevel >= 2) mciwaveDbgOut _x_
#define dprintf3( _x_ ) if (mciwaveDebugLevel >= 3) mciwaveDbgOut _x_
#define dprintf4( _x_ ) if (mciwaveDebugLevel >= 4) mciwaveDbgOut _x_
#else // DBG
#define mciwaveInitDebugLevel() 0
#define WinAssert(exp) 0
#define WinEval(exp) (exp)
#define dprintf(x)
#define dprintf1(x)
#define dprintf2(x)
#define dprintf3(x)
#define dprintf4(x)
#endif
#endif // MCIWAVE_H