/* ** 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 #include #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 WAVEDATANODE.dDataLength 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 TEMPDATAMASK. The length can be accessed by using UNMASKDATASTART. @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 WAVEDATANODE.dDataLength 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 ENDOFNODES 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 DIRECTION | 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 mwOpenDevice before the actual task is created by mmTaskCreate. After the task is created, mwOpenDevice 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 | mwCloseDevice 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 LPWAVEHDR | 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 INFINITEFILESIZE 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 HMMIO | 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 LPMMIOPROC | pIOProc | Contains a pointer to the alternate MMIO IO procedure, if any, else NULL. @field LPWAVEDATANODE | 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 WAVEFORMAT | 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 WAVEDESC 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