Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

2026 lines
65 KiB

/****************************************************************************
*
* wavedd.c
*
* Multimedia kernel driver support component (mmdrv)
*
* Copyright (c) 1991-1996 Microsoft Corporation
*
* Driver for wave input and output devices
*
* -- Wave driver entry points (wodMessage, widMessage)
* -- Auxiliary task (necessary for receiving Apcs and generating
* callbacks ASYNCRHONOUSLY)
* -- Interface to kernel driver (via DeviceIoControl)
*
* Note that if any error occurs then the kernel device is closed
* and all subsequent calls requiring calls to the kernel device
* return error until the device is closed by the application.
*
* History
* 01-Feb-1992 - Robin Speed (RobinSp) wrote it
* 04-Feb-1992 - SteveDav reviewed it
* 08-Feb-1992 - RobinSp - Redesign to chop up caller's data.
* Also does loops so we can remove them from the
* kernel driver.
*
***************************************************************************/
#include "drvlib.h"
#include <ntddwave.h>
#include <mmreg.h>
/*****************************************************************************
internal declarations
****************************************************************************/
// Stack size for our auxiliary task
#define WAVE_STACK_SIZE 300
typedef enum {
WaveThreadInvalid,
WaveThreadAddBuffer,
WaveThreadSetState,
WaveThreadSetData,
WaveThreadGetData,
WaveThreadBreakLoop,
WaveThreadClose,
WaveThreadTerminate
} WAVETHREADFUNCTION;
#define MAX_BUFFER_SIZE 8192 // Largest buffer we send to device
#define MAX_WAVE_BYTES 5*8192 // Max bytes we have queued on was 22000
//
// Structure to hide our overlapped structure in so we can get some
// context on IO completion
//
typedef struct {
OVERLAPPED Ovl;
LPWAVEHDR WaveHdr;
} WAVEOVL, *PWAVEOVL;
// per allocation structure for wave
typedef struct tag_WAVEALLOC {
struct tag_WAVEALLOC *Next; // Chaining
UINT DeviceNumber; // Which device
UINT DeviceType; // WaveInput or WaveOutput
DWORD_PTR dwCallback; // client's callback
DWORD_PTR dwInstance; // client's instance data
DWORD dwFlags; // Open flags
HWAVE hWave; // handle for stream
DWORD dwStatus; // Status bits (LowPri, etc)
HANDLE hDev; // Wave device handle
LPWAVEHDR DeviceQueue; // Buffers queued by application
LPWAVEHDR NextBuffer; // Next buffer to send to device
DWORD BufferPosition; // How far we're into a large buffer
DWORD BytesOutstanding;
// Bytes being processed by device
LPWAVEHDR LoopHead; // Start of loop if any
DWORD LoopCount; // Number more loops to go
WAVEOVL DummyWaveOvl; // For break loop
//
HANDLE Event; // Event for driver syncrhonization
// and notification of auxiliary
// task operation completion.
WAVETHREADFUNCTION AuxFunction; // Function for thread to perform
union {
LPWAVEHDR pHdr; // Buffer to pass in aux task
ULONG State; // State to set
struct {
ULONG Function; // IOCTL to use
PBYTE pData; // Data to set or get
ULONG DataLen; // Length of data
} GetSetData;
} AuxParam;
// 0 means terminate task.
HANDLE AuxEvent1; // Aux thread waits on this
HANDLE AuxEvent2; // Caller of Aux thread waits on this
HANDLE ThreadHandle; // Handle for thread termination ONLY
MMRESULT AuxReturnCode; // Return code from Aux task
}WAVEALLOC, *PWAVEALLOC;
PWAVEALLOC WaveHandleList; // Our chain of wave handles
//
// extra flag to track buffer completion
//
#define WHDR_COMPLETE 0x80000000
//
// Status bits for WAVEALLOC.dwStatus
//
#define WAVEALLOC_STATUS_LOWPRIORITY 0x00000001
/*****************************************************************************
internal function prototypes
****************************************************************************/
STATIC MMRESULT waveGetDevCaps(DWORD id, UINT DeviceType, LPBYTE lpCaps,
DWORD dwSize);
STATIC DWORD waveThread(LPVOID lpParameter);
STATIC void waveCleanUp(PWAVEALLOC pClient);
STATIC MMRESULT waveThreadCall(WAVETHREADFUNCTION Function, PWAVEALLOC pClient);
STATIC MMRESULT waveSetState(PWAVEALLOC pClient, ULONG State);
STATIC MMRESULT waveWrite(LPWAVEHDR pHdr, PWAVEALLOC pClient);
STATIC void waveBlockFinished(LPWAVEHDR lpHdr, DWORD MsgId);
STATIC void waveCallback(PWAVEALLOC pWave, DWORD msg, DWORD_PTR dw1);
STATIC void waveCompleteBuffers(PWAVEALLOC pClient);
STATIC void waveFreeQ(PWAVEALLOC pClient);
STATIC void waveOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped);
/* Attempt to pre-touch up to MIN(iSize,PRETOUCHLIMIT) bytes on from pb.
If AllowFault then keep going to fault the stuff in.
Otherwise stop as soon as you notice the clock ticking
*/
//PreTouch(BYTE * pb, int iSize, BOOL AllowFault)
//{
// DWORD dwTicks = GetTickCount();
// int pages = 0;
// static int Headway[100];
// static int c = 0;
// static int TotalTouches = 0;
// static int TimesThrough = 0; // number of times this code has run.
//
// if (iSize > PRETOUCHLIMIT) {
// iSize = PRETOUCHLIMIT;
// }
//
// ++TimesThrough;
//
// // pre-touch the pages but get out if it's taking too long
// // (which probably means we took a page fault.
// // Touch at least 2 pages as we may want 2 pages per DMA 1/2 buffer.
// while (iSize>0) {
// volatile BYTE b;
// b = *pb;
// pb += 4096; // move to next page. Are they ALWAYS 4096?
// iSize -= 4096; // and count it off
// ++pages;
// ++TotalTouches;
// if (dwTicks<GetTickCount() && pages>1 && !AllowFault) break;
// }
// Headway[c] = pages;
// ++c;
//
// if (c==100){
// for (c=0; c<=99; c += 10){
// dprintf(("%5ld %5ld %5ld %5ld %5ld %5ld %5ld %5ld %5ld %5ld",Headway[c],Headway[c+1],Headway[c+2],Headway[c+3],Headway[c+4],Headway[c+5],Headway[c+6],Headway[c+7],Headway[c+8],Headway[c+9]));
// }
// dprintf((" "));
// c = 0;
// }
//}
/****************************************************************************
* @doc INTERNAL
*
* @api VOID | TerminateWave | Free all wave resources
*
* @rdesc None
***************************************************************************/
VOID TerminateWave(VOID)
{
#ifdef TERMINATE
//
// This is all wrong - we need to find out how to terminate threads !
//
PWAVEALLOC pClient;
//
// Remove all our threads and their resources
//
for (pClient = WaveHandleList; pClient != NULL; pClient = pClient->Next) {
if (pClient->ThreadHandle) {
//
// Kill our thread. But be careful ! It may
// already have gone away - so don't wait for
// it to set its event, just wait for it
// to finish
//
//
// Set the function code
//
pClient->AuxFunction = WaveThreadTerminate;
//
// Kick off the thread
//
SetEvent(pClient->AuxEvent1);
//
// We created our threads with mmTaskCreate so it's
// safe to wait on them
//
WaitForSingleObject(pClient->ThreadHandle, INFINITE);
}
waveCleanUp(pClient);
}
#endif
}
/****************************************************************************
* @doc INTERNAL
*
* @api void | waveGetDevCaps | Get the device capabilities.
*
* @parm DWORD | id | Device id
*
* @parm UINT | DeviceType | type of device
*
* @parm LPBYTE | lpCaps | Far pointer to a WAVEOUTCAPS structure to
* receive the information.
*
* @parm DWORD | dwSize | Size of the WAVEOUTCAPS structure.
*
* @rdesc MMSYS.. return code.
***************************************************************************/
STATIC MMRESULT waveGetDevCaps(DWORD id, UINT DeviceType,
LPBYTE lpCaps, DWORD dwSize)
{
MMRESULT mrc;
if (DeviceType == WAVE_OUT) {
WAVEOUTCAPSW wc;
mrc = sndGetData(DeviceType, id, sizeof(wc), (LPBYTE)&wc,
IOCTL_WAVE_GET_CAPABILITIES);
if (mrc != MMSYSERR_NOERROR) {
return mrc;
}
InternalLoadString((UINT)*(LPDWORD)wc.szPname, wc.szPname,
sizeof(wc.szPname) / sizeof(WCHAR));
CopyMemory(lpCaps, &wc, min(sizeof(wc), dwSize));
} else {
WAVEINCAPSW wc;
mrc = sndGetData(DeviceType, id, sizeof(wc), (LPBYTE)&wc,
IOCTL_WAVE_GET_CAPABILITIES);
if (mrc != MMSYSERR_NOERROR) {
return mrc;
}
InternalLoadString((UINT)*(LPDWORD)wc.szPname, wc.szPname,
sizeof(wc.szPname) / sizeof(WCHAR));
CopyMemory(lpCaps, &wc, min(sizeof(wc), dwSize));
}
return MMSYSERR_NOERROR;
}
/****************************************************************************
* @doc INTERNAL
*
* @api DWORD | waveGetPos | Get the stream position in samples.
*
* @parm PWAVEALLOC | pClient | Client handle.
*
* @parm LPMMTIME | lpmmt | Far pointer to an MMTIME structure.
*
* @parm DWORD | dwSize | Size of the MMTIME structure.
*
* @rdesc MMSYS... return value.
***************************************************************************/
MMRESULT waveGetPos(PWAVEALLOC pClient, LPMMTIME lpmmt, DWORD dwSize)
{
WAVE_DD_POSITION PositionData;
MMRESULT mErr;
if (dwSize < sizeof(MMTIME))
return MMSYSERR_ERROR;
//
// Get the current position from the driver
//
mErr = sndGetHandleData(pClient->hDev,
sizeof(PositionData),
&PositionData,
IOCTL_WAVE_GET_POSITION,
pClient->Event);
if (mErr == MMSYSERR_NOERROR) {
if (lpmmt->wType == TIME_BYTES) {
lpmmt->u.cb = PositionData.ByteCount;
}
// default is samples.
else {
lpmmt->wType = TIME_SAMPLES;
lpmmt->u.sample = PositionData.SampleCount;
}
}
return mErr;
}
/****************************************************************************
* @doc INTERNAL
*
* @api MMRESULT | waveOpen | Open wave device and set up logical device data
* and auxilary task for issuing requests and servicing Apc's
*
* @parm WAVEDEVTYPE | DeviceType | Whether it's a wave input or output device
*
* @parm DWORD | id | The device logical id
*
* @parm DWORD | msg | Input parameter to wodMessage
*
* @parm DWORD | dwUser | Input parameter to wodMessage - pointer to
* application's handle (generated by this routine)
*
* @parm DWORD | dwParam1 | Input parameter to wodMessage
*
* @parm DWORD | dwParam2 | Input parameter to wodMessage
*
* @rdesc wodMessage return code.
***************************************************************************/
STATIC MMRESULT waveOpen(UINT DeviceType,
DWORD id,
DWORD_PTR dwUser,
DWORD_PTR dwParam1,
DWORD_PTR dwParam2)
{
PWAVEALLOC pClient; // pointer to client information structure
MMRESULT mRet;
BOOL Result;
DWORD BytesReturned;
LPWAVEFORMATEX Format;
Format = (LPWAVEFORMATEX)((LPWAVEOPENDESC)dwParam1)->lpFormat;
// dwParam1 contains a pointer to a WAVEOPENDESC
// dwParam2 contains wave driver specific flags in the LOWORD
// and generic driver flags in the HIWORD
//
// If it's only a query to check if the device supports our format
// we :
// Open the device
// Test the format
// Close the device
//
if (dwParam2 & WAVE_FORMAT_QUERY) {
HANDLE hDev;
//
// See if we can open our device
// Only open for read (this should always work for our devices
// unless there are system problems).
//
mRet = sndOpenDev(DeviceType,
id,
&hDev,
GENERIC_READ);
if (mRet != MMSYSERR_NOERROR) {
D2(("drvlib: waveOpen, device=%x, QUERY failed to open",id));
return mRet;
}
//
// Check the format
//
Result = DeviceIoControl(
hDev,
IOCTL_WAVE_QUERY_FORMAT,
(PVOID)Format,
Format->wFormatTag == WAVE_FORMAT_PCM ?
sizeof(PCMWAVEFORMAT) :
sizeof(WAVEFORMATEX) + Format->cbSize,
// Input buffer size
NULL, // Output buffer
0, // Output buffer size
&BytesReturned,
NULL);
//
// Only a query so close the device
//
CloseHandle(hDev);
#if DBG
{
MMRESULT mmr;
mmr = Result ? MMSYSERR_NOERROR :
GetLastError() == ERROR_NOT_SUPPORTED ? WAVERR_BADFORMAT :
sndTranslateStatus();
D2(("drvlib: waveOpen, device=%x, QUERY returning %x",id, mmr));
return(mmr);
}
#else
return Result ? MMSYSERR_NOERROR :
GetLastError() == ERROR_NOT_SUPPORTED ? WAVERR_BADFORMAT :
sndTranslateStatus();
#endif
}
//
// See if we've got this device already in our list (in
// which case we have a thread and events for it already made)
//
EnterCriticalSection(&mmDrvCritSec);
for (pClient = WaveHandleList;
pClient != NULL;
pClient = pClient->Next) {
if (pClient->DeviceNumber == id &&
pClient->DeviceType == DeviceType &&
(!(pClient->dwStatus & WAVEALLOC_STATUS_LOWPRIORITY)) ) {
//
// We already have a thread and resources for this device
//
if (pClient->hDev != INVALID_HANDLE_VALUE) {
//
// Someone else is using it!
//
LeaveCriticalSection(&mmDrvCritSec);
return MMSYSERR_ALLOCATED;
}
break;
}
}
//
// allocate my per-client structure and zero it (LPTR).
//
if (pClient == NULL) {
pClient = (PWAVEALLOC)HeapAlloc(hHeap, 0, sizeof(WAVEALLOC));
if (pClient == NULL) {
LeaveCriticalSection(&mmDrvCritSec);
D3(("waveopen failing... NOMEM"));
return MMSYSERR_NOMEM;
}
dprintf2(("Creating new device resource for device id %d, type %s",
id,
DeviceType == WAVE_IN ? "Wave Input" : "Wave Output"));
memset((PVOID)pClient, 0, sizeof(WAVEALLOC));
// Make it look free
pClient->hDev = INVALID_HANDLE_VALUE;
//
// Add it to the list
//
pClient->DeviceNumber = id;
pClient->DeviceType = DeviceType;
pClient->Next = WaveHandleList;
WaveHandleList = pClient;
} else {
dprintf2(("Reusing old device resource for device id %d, type %s",
id,
DeviceType == WAVE_IN ? "Wave Input" : "Wave Output"));
}
//
// and fill it with info
//
pClient->dwCallback = ((LPWAVEOPENDESC)dwParam1)->dwCallback;
pClient->dwInstance = ((LPWAVEOPENDESC)dwParam1)->dwInstance;
pClient->hWave = ((LPWAVEOPENDESC)dwParam1)->hWave;
pClient->dwFlags = (DWORD)dwParam2;
// pClient->hDev is initialized by sndOpenDev
pClient->DeviceQueue = NULL;
pClient->NextBuffer = NULL;
pClient->BufferPosition = 0;
pClient->BytesOutstanding = 0;
pClient->LoopHead = NULL;
pClient->LoopCount = 0;
//
// See if we can open our device
// We could get ERROR_BUSY if someone else has the device open
// for writing.
//
mRet = sndOpenDev(DeviceType,
id,
&pClient->hDev,
(GENERIC_READ | GENERIC_WRITE));
if (mRet != MMSYSERR_NOERROR) {
WinAssert(pClient->hDev == INVALID_HANDLE_VALUE);
LeaveCriticalSection(&mmDrvCritSec);
return mRet;
}
//
// make sure we can handle the format and set it.
//
Result = DeviceIoControl(
pClient->hDev,
IOCTL_WAVE_SET_FORMAT,
(PVOID)Format,
Format->wFormatTag == WAVE_FORMAT_PCM ?
sizeof(PCMWAVEFORMAT) :
sizeof(WAVEFORMATEX) + Format->cbSize,
NULL, // Output buffer
0, // Output buffer size
&BytesReturned,
NULL);
if (!Result) {
CloseHandle(pClient->hDev);
pClient->hDev = INVALID_HANDLE_VALUE;
LeaveCriticalSection(&mmDrvCritSec);
return GetLastError() == ERROR_NOT_SUPPORTED ? WAVERR_BADFORMAT :
sndTranslateStatus();
}
LeaveCriticalSection(&mmDrvCritSec);
//
// Create our event for synchronization with the kernel driver
//
if (!pClient->Event) {
pClient->Event = CreateEvent(NULL, FALSE, FALSE, NULL);
if (pClient->Event == NULL) {
waveCleanUp(pClient);
return MMSYSERR_NOMEM;
}
//
// Create our event for our thread to wait on
//
pClient->AuxEvent1 = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!pClient->AuxEvent1) {
waveCleanUp(pClient);
return MMSYSERR_NOMEM;
}
//
// Create our event for waiting for the auxiliary thread
//
pClient->AuxEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!pClient->AuxEvent2) {
waveCleanUp(pClient);
return MMSYSERR_NOMEM;
}
//
// Create our auxiliary thread for sending buffers to the driver
// and collecting Apcs
//
mRet = mmTaskCreate((LPTASKCALLBACK)waveThread,
&pClient->ThreadHandle,
(DWORD_PTR)pClient);
if (mRet != MMSYSERR_NOERROR) {
waveCleanUp(pClient);
return MMSYSERR_NOMEM;
}
//
// Make sure the thread has really started
//
WaitForSingleObject(pClient->AuxEvent2, INFINITE);
}
//
// give the client my driver dw
//
{
PWAVEALLOC *pUserHandle;
pUserHandle = (PWAVEALLOC *)dwUser;
*pUserHandle = pClient;
}
//
// sent client his OPEN callback message
//
waveCallback(pClient, DeviceType == WAVE_OUT ? WOM_OPEN : WIM_OPEN, 0L);
return MMSYSERR_NOERROR;
}
/****************************************************************************
* @doc INTERNAL
*
* @api void | waveCleanUp | Free resources for a wave device
*
* @parm PWAVEALLOC | pClient | Pointer to a WAVEALLOC structure describing
* resources to be freed.
*
* @rdesc There is no return value.
*
* @comm If the pointer to the resource is NULL then the resource has not
* been allocated.
***************************************************************************/
STATIC void waveCleanUp(PWAVEALLOC pClient)
{
EnterCriticalSection(&mmDrvCritSec);
if (pClient->hDev != INVALID_HANDLE_VALUE) {
CloseHandle(pClient->hDev);
pClient->hDev = INVALID_HANDLE_VALUE;
}
if (pClient->AuxEvent1) {
CloseHandle(pClient->AuxEvent1);
pClient->AuxEvent1 = NULL;
}
if (pClient->AuxEvent2) {
CloseHandle(pClient->AuxEvent2);
pClient->AuxEvent2 = NULL;
}
if (pClient->Event) {
CloseHandle(pClient->Event);
pClient->Event = NULL;
}
//
// Clean up low priority flag or our thread will be lost forever
//
pClient->dwStatus = 0;
LeaveCriticalSection(&mmDrvCritSec);
}
/****************************************************************************
* @doc INTERNAL
*
* @api MMRESULT | waveWrite | Pass a new buffer to the Auxiliary thread for
* a wave device.
*
* @parm LPWAVEHDR | pHdr | Pointer to a wave buffer
*
* @parm PWAVEALLOC | pClient | The data associated with the logical wave
* device.
*
* @rdesc A MMSYS... type return code for the application.
*
* @comm The buffer flags are set and the buffer is passed to the auxiliary
* device task for processing.
***************************************************************************/
STATIC MMRESULT waveWrite(LPWAVEHDR pHdr, PWAVEALLOC pClient)
{
//
// Put the request at the end of our queue.
//
pHdr->dwFlags |= WHDR_INQUEUE;
pHdr->dwFlags &= ~WHDR_DONE;
pClient->AuxParam.pHdr = pHdr;
return waveThreadCall(WaveThreadAddBuffer, pClient);
}
/****************************************************************************
* @doc INTERNAL
*
* @api MMRESULT | waveSetState | Set a wave device to a given state
* This function is executed on the Auxiliary thread to synchronize
* correctly.
*
* @parm PWAVEALLOC | pClient | The data associated with the logical wave
* output device.
*
* @parm ULONG | State | The new state
*
* @rdesc A MMSYS... type return code for the application.
***************************************************************************/
STATIC MMRESULT waveSetState(PWAVEALLOC pClient, ULONG State)
{
return sndSetHandleData(pClient->hDev,
sizeof(State),
&State,
IOCTL_WAVE_SET_STATE,
pClient->Event);
}
/****************************************************************************
* @doc INTERNAL
*
* @api void | waveBlockFinished | This function sets the done bit and invokes
* the callback function if there is one.
*
* @parm LPWAVEHDR | lpHdr | Far pointer to the header.
*
* @rdesc There is no return value.
***************************************************************************/
STATIC void waveBlockFinished(LPWAVEHDR lpHdr, DWORD MsgId)
{
PWAVEALLOC pWav;
D3(("blkfin: lpHdr = %x", lpHdr));
// Clear our private flag
lpHdr->dwFlags &= ~WHDR_COMPLETE;
// We are giving the block back to the application. The header is no
// longer in our queue, so we reset the WHDR_INQUEUE bit. Also, we
// clear our driver specific bit and cauterize the lpNext pointer.
lpHdr->dwFlags &= ~WHDR_INQUEUE;
lpHdr->lpNext = NULL;
pWav = (PWAVEALLOC)(lpHdr->reserved);
// set the 'done' bit - note that some people poll this bit.
lpHdr->dwFlags |= WHDR_DONE;
// invoke the callback function
waveCallback(pWav, MsgId, (DWORD_PTR)lpHdr);
}
/****************************************************************************
* @doc INTERNAL
*
* @api MMRESULT | waveThreadCall | Set the function for the thread to perform
* and 'call' the thread using the event pair mechanism.
*
* @parm WAVETHREADFUNCTION | Function | The function to perform
*
* @parm PWAVEALLOC | Our logical device data
*
* @rdesc An MMSYS... type return value suitable for returning to the
* application
*
* @comm The AuxParam field in the device data is the 'input' to
* the function processing loop in WaveThread().
***************************************************************************/
STATIC MMRESULT waveThreadCall(WAVETHREADFUNCTION Function, PWAVEALLOC pClient)
{
//
// Trap any failures
//
WinAssert(pClient->hDev != INVALID_HANDLE_VALUE);
//
// Set the function code
//
pClient->AuxFunction = Function;
//
// Kick off the thread
//
SetEvent(pClient->AuxEvent1);
//
// Wait for it to complete
//
WaitForSingleObject(pClient->AuxEvent2, INFINITE);
//
// Return the return code that our task set.
//
D3(("waveThreadCall: function==%x, return=%x", Function, pClient->AuxReturnCode));
return pClient->AuxReturnCode;
}
/****************************************************************************
* @doc INTERNAL
*
* @api MMRESULT | wavePartialApc | Called when a partial buffer is complete.
*
* @parm DWORD | BytesTransferred | Not relevant to us
*
* @parm LPOVERLAPPED | pOverLapped | Overlapped structure for this callback
*
* @rdesc None
*
* @comm The IO status block is freed and the BytesOutstanding count
* used to limit the buffers we have locked down is updated (we
* know here that parital buffers are all the same size).
* Also the byte count for a recording buffer is updated.
***************************************************************************/
STATIC void wavePartialOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped)
{
LPWAVEHDR pHdr;
PWAVEALLOC pClient;
pHdr = ((PWAVEOVL)pOverlapped)->WaveHdr;
D3(("wavePartialOvl: pHdr = %x", pHdr));
pClient = (PWAVEALLOC)pHdr->reserved;
//
// We can't trust the IO system to return our buffers in the right
// order so we set a flag in the buffer but only complete buffers
// at the FRONT of the queue which have the flag set. In fact
// we don't process the stuff here - leave that for when we
// exit the wait because calling the client's callback can
// do nasty things inside and Apc routine
//
WinAssert(pHdr->dwFlags & WHDR_INQUEUE);
WinAssert(!(pHdr->dwFlags & WHDR_COMPLETE));
//
// Recalculate how many bytes are outstanding on the device
//
pClient->BytesOutstanding -= MAX_BUFFER_SIZE;
//
// Work out how much was recorded if we're a recording device
//
if (pClient->DeviceType == WAVE_IN) {
pHdr->dwBytesRecorded += BytesTransferred;
}
//
// Free our Iosb
//
HeapFree(hHeap, 0, (LPSTR)pOverlapped);
}
/****************************************************************************
* @doc INTERNAL
*
* @api void | waveOvl | Called when a (user) buffer is complete.
*
* @parm DWORD | BytesTransferred | Not relevant to us
*
* @parm LPOVERLAPPED | pOverLapped | Overlapped structure for this callback
*
* @parm PIO_STATUS_BLOCK | The Io status block we used
*
* @rdesc None
*
* @comm The IO status block is freed and the BytesOutstanding count
* used to limit the buffers we have locked down is updated (we
* know here that parital buffers are all the same size so we
* can compute the size of the 'last' buffer for a given user buffer).
* Also the byte count for a recording buffer is updated.
* The user buffer is marked as 'DONE'.
***************************************************************************/
STATIC void waveOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped)
{
PWAVEHDR pHdr;
PWAVEALLOC pClient;
pHdr = ((PWAVEOVL)pOverlapped)->WaveHdr;
D3(("waveOvl: pHdr = %x", pHdr));
pClient = (PWAVEALLOC)pHdr->reserved;
//
// We can't trust the IO system to return our buffers in the right
// order so we set a flag in the buffer but only complete buffers
// at the FRONT of the queue which have the flag set. In fact
// we don't process the stuff here - leave that for when we
// exit the wait because calling the client's callback can
// do nasty things inside and Apc routine
//
WinAssert(pHdr->dwFlags & WHDR_INQUEUE);
WinAssert(!(pHdr->dwFlags & WHDR_COMPLETE));
//
// Mark buffer as done unless we're doing more loops with it
//
pHdr->dwFlags |= WHDR_COMPLETE;
//
// It's now our duty to see if there were some old loops lying
// around earlier in the queue which are vestiges of old loops.
//
if (pHdr->dwFlags & WHDR_BEGINLOOP) {
PWAVEHDR pHdrSearch;
for (pHdrSearch = pClient->DeviceQueue ;
pHdrSearch != pHdr ;
pHdrSearch = pHdrSearch->lpNext) {
WinAssert(pHdrSearch != NULL);
pHdrSearch->dwFlags |= WHDR_COMPLETE;
}
}
//
// Recalculate how many bytes are outstanding on the device
//
if (pHdr->dwBufferLength) {
pClient->BytesOutstanding -= (pHdr->dwBufferLength - 1) %
MAX_BUFFER_SIZE + 1;
}
//
// Work out how much was recorded if we're a recording device
//
if (pClient->DeviceType == WAVE_IN) {
pHdr->dwBytesRecorded += BytesTransferred;
}
//
// Free our Iosb
//
HeapFree(hHeap, 0, (LPSTR)pOverlapped);
}
/****************************************************************************
* @doc INTERNAL
*
* @api MMRESULT | waveLoopOvl | Called when a (user) buffer is complete.
* but the buffer was need for more loops.
*
* @parm DWORD | BytesTransferred | Not relevant to us
*
* @parm LPOVERLAPPED | pOverLapped | Overlapped structure for this callback
*
* @rdesc None
*
* @comm Same as waveApc but the buffer is not marked complete.
***************************************************************************/
STATIC void waveLoopOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped)
{
DWORD dwFlags;
PWAVEHDR pHdr;
D3(("waveLoopOvl"));
pHdr = ((PWAVEOVL)pOverlapped)->WaveHdr;
//
// Do it this way to avoid falling into a hole if the Apcs are
// in the wrong order !!!
//
dwFlags = pHdr->dwFlags;
waveOvl(dwErrorCode, BytesTransferred, pOverlapped);
pHdr->dwFlags = dwFlags;
}
/****************************************************************************
* @doc INTERNAL
*
* @api MMRESULT | waveBreakOvl | Used to chase out a buffer to break a loop.
*
* @parm DWORD | BytesTransferred | Not relevant to us
*
* @parm LPOVERLAPPED | pOverLapped | Overlapped structure for this callback
*
* @rdesc None
*
* @comm Mark the relevant buffer complete
***************************************************************************/
STATIC void waveBreakOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped)
{
D3(("waveBreakOvl"));
((PWAVEOVL)pOverlapped)->WaveHdr->dwFlags |= WHDR_COMPLETE;
}
/****************************************************************************
* @doc INTERNAL
*
* @api void | waveStart | Send more buffers to the device if possible
*
* @parm PWAVEALLOC | pClient | The client's handle data
*
* @rdesc There is no return code.
*
* @comm The routine is called both when new buffers become available
* or when old buffers or parital buffers are completed so
* that device can accept more data.
*
* No more that MAX_WAVE_BYTES in buffers no bigger than
* MAX_BUFFER_SIZE are to be outstanding on the device at
* any one time.
*
* An additional complication is that we have to process loops
* which means (among other things) that the SAME BUFFER may
* appear multiple times in the driver's list (as different
* requests). There are no loops for input devices.
* Loop buffers complete with Apcs which do not complete them
* (except for the final loop iteration) which means that if
* we decide unexpectedly to finish a loop (ie by waveOutBreakLoop)
* we must 'chase' the loop out with an artificial buffer to
* get our Apc going.
*
***************************************************************************/
STATIC MMRESULT waveStart(PWAVEALLOC pClient)
{
DWORD dwSize;
BOOL Result;
//
// See if we can fit any more data on the device
//
WinAssert(pClient->hDev != INVALID_HANDLE_VALUE);
while (pClient->NextBuffer) {
PWAVEHDR pHdr;
pHdr = pClient->NextBuffer;
WinAssert(pClient->DeviceQueue != NULL);
WinAssert(!(pHdr->dwFlags & (WHDR_DONE | WHDR_COMPLETE)));
dwSize = pHdr->dwBufferLength - pClient->BufferPosition;
if (dwSize > MAX_BUFFER_SIZE) {
dwSize = MAX_BUFFER_SIZE;
}
if (dwSize + pClient->BytesOutstanding <= MAX_WAVE_BYTES) {
//
// OK - we can fit another buffer in
//
// Don't have our overlay structure on the stack for an
// ASYNCHRONOUS IO ! Otherwise the IO subsystem will overwrite
// somebody else's data when the operation completes
//
PWAVEOVL pWaveOvl;
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine;
if (pClient->BufferPosition == 0) {
//
// Start of new buffer
// See if the buffer is the start of a new loop
// (Check not continuation of old one)
//
if (pClient->NextBuffer &&
(pClient->NextBuffer->dwFlags & WHDR_BEGINLOOP) &&
pClient->NextBuffer != pClient->LoopHead) {
pClient->LoopHead = pClient->NextBuffer;
pClient->LoopCount = pClient->NextBuffer->dwLoops;
//
// Loop count is number of times to play
//
if (pClient->LoopCount > 0) {
pClient->LoopCount--;
}
}
//
// See if the loop is actually finished
//
if (pClient->LoopCount == 0) {
pClient->LoopHead = NULL;
}
}
pWaveOvl = (PWAVEOVL)HeapAlloc(hHeap, 0, sizeof(*pWaveOvl));
if (pWaveOvl == NULL) {
return MMSYSERR_NOMEM;
}
memset((PVOID)pWaveOvl, 0, sizeof(*pWaveOvl));
pWaveOvl->WaveHdr = pHdr;
lpCompletionRoutine = pHdr->dwBufferLength !=
pClient->BufferPosition + dwSize ?
wavePartialOvl :
NULL != pClient->LoopHead ?
waveLoopOvl :
waveOvl;
if (pClient->DeviceType == WAVE_OUT) {
Result = WriteFileEx(
pClient->hDev,
(PBYTE)pHdr->lpData + // Output buffer
pClient->BufferPosition,
dwSize,
(LPOVERLAPPED)pWaveOvl, // Overlap structure
lpCompletionRoutine); // Overlap callback
} else {
Result = ReadFileEx(
pClient->hDev,
(PBYTE)pHdr->lpData + // Output buffer
pClient->BufferPosition,
dwSize,
(LPOVERLAPPED)pWaveOvl, // Overlap structure
lpCompletionRoutine); // Overlap callback
}
dprintf3(("Sent/Read %u wave bytes to device, return code %8X",
dwSize, GetLastError()));
if (!Result && GetLastError() != ERROR_IO_PENDING) {
//
// Free the Iosb since we won't be getting any callbacks
//
HeapFree(hHeap, 0, (LPSTR)pWaveOvl);
//
// If the driver has not got any bytes outstanding then
// everything may grind to a halt so release everything
// here and notify 'completion' (ie mark all buffers
// complete). This is unsatisfactory but there's no
// way of telling the application what happenend.
//
if (pClient->BytesOutstanding == 0) {
//
// This will cause acknowlegements to be made when
// waveCompleteBuffers is run
//
waveFreeQ(pClient);
}
return sndTranslateStatus();
} else {
//
// We successfully queued the buffer
// Update our local data
//
pClient->BytesOutstanding += dwSize;
pClient->BufferPosition += dwSize;
if (pClient->BufferPosition == pHdr->dwBufferLength) {
//
// Finished this buffer - move on to the next
//
if (!pClient->LoopHead ||
!(pHdr->dwFlags & WHDR_ENDLOOP)) {
//
// Not end of in a loop so we can free this buffer
//
pClient->NextBuffer = pHdr->lpNext;
} else {
//
// Finished a loop
//
if (pClient->LoopCount != 0) {
pClient->LoopCount--;
pClient->NextBuffer = pClient->LoopHead;
} else {
//
// Someone's tried to kill us. We have
// to 'chase out' the start of this loop
// so send a dummy (NULL) packet at the
// back of the driver's queue
//
pClient->DummyWaveOvl.WaveHdr = pClient->LoopHead;
Result =
WriteFileEx(
pClient->hDev,
(PVOID)pHdr->lpData,
0,
&pClient->DummyWaveOvl.Ovl, // Static for async
waveBreakOvl);
if (Result || GetLastError() == ERROR_IO_PENDING) {
pClient->LoopHead = NULL; // Loop complete
pClient->NextBuffer = pHdr->lpNext;
}
}
}
pClient->BufferPosition = 0;
}
}
{
// /* Before we go home, let's just touch ONE page - if there is one */
// PBYTE pb = (PBYTE)pHdr->lpData + pClient->BufferPosition;
// pb = ((DWORD)pb & 0xFFFFF000) + 0x1000; /* find page start of next page */
//
// if ( (PBYTE)pHdr->lpData + pHdr->dwBufferLength > pb )
// PreTouch( pb, 1, FALSE);
// /* Before we go home, let's just try to pre-touch that which we will soon want */
// PreTouch( (PBYTE)pHdr->lpData + pClient->BufferPosition
// , pHdr->dwBufferLength - pClient->BufferPosition
// , FALSE
// );
}
} else {
//
// Cannot fit any more bytes in at the moment
//
// /* Before we go home, let's just try to pre-touch that which we will soon want */
// PreTouch( (PBYTE)pHdr->lpData + pClient->BufferPosition
// , pHdr->dwBufferLength - pClient->BufferPosition
// , FALSE
// );
/* NOW go home! */
break;
}
}
return MMSYSERR_NOERROR;
}
/****************************************************************************
* @doc INTERNAL
*
* @api void | waveCompleteBuffers | Buffer completion routine. This completes
* the work of the Apc routine at below Apc priority. This gets
* round the nasty situations arising when the user's callback
* causes more apcs to run (I strongly suspect this is a kernel
* bug).
*
* @parm PWAVEALLOC | pClient | The client's handle data
*
* @rdesc There is no return code.
***************************************************************************/
STATIC void waveCompleteBuffers(PWAVEALLOC pClient)
{
//
// Process buffers from the front of our queue unless we're in
// a loop
//
while (pClient->DeviceQueue &&
(pClient->DeviceQueue->dwFlags & WHDR_COMPLETE)) {
PWAVEHDR pHdr;
pHdr = pClient->DeviceQueue;
//
// Release buffer
//
pClient->DeviceQueue = pHdr->lpNext;
//
// Complete our buffer - note - this can cause another
// buffer to be marked as complete if the client's
// callback runs into an alertable wait.
//
waveBlockFinished(pHdr,
pClient->DeviceType == WAVE_OUT ?
WOM_DONE : WIM_DATA);
}
//
// We might be able to start some more output at this point
//
waveStart(pClient);
}
/****************************************************************************
* @doc INTERNAL
*
* @api void | waveFreeQ | Mark all outstanding buffers complete
*
* @parm PWAVEALLOC | pClient | The client's handle data
*
* @rdesc There is no return code.
***************************************************************************/
STATIC void waveFreeQ(PWAVEALLOC pClient)
{
PWAVEHDR pHdr;
for (pHdr = pClient->DeviceQueue;
pHdr != NULL;
pHdr = pHdr->lpNext) {
pHdr->dwFlags |= WHDR_COMPLETE;
}
//
// Tidy up next buffer
//
pClient->NextBuffer = NULL;
pClient->BufferPosition = 0;
}
#if 0
typedef struct {
LPBYTE Addr;
DWORD Len;
} PRETOUCHTHREADPARM;
/* asynchronous pre-toucher thread */
DWORD PreToucher(DWORD dw)
{
PRETOUCHTHREADPARM * pttp;
int iSize;
BYTE * pb;
pttp = (PRETOUCHTHREADPARM *) dw;
iSize = pttp->Len;
pb = pttp->Addr;
LocalFree(pttp);
while (iSize>0) {
volatile BYTE b;
b = *pb;
pb += 4096; // move to next page. Are they ALWAYS 4096?
iSize -= 4096; // and count it off
}
dprintf(("All pretouched!"));
return 0;
}
#endif //0
/****************************************************************************
* @doc INTERNAL
*
* @api DWORD | waveThread | Wave device auxiliary thread.
*
* @parm LPVOID | lpParameter | The thread parameter. In our case this is a
* pointer to our wave device data.
*
* @rdesc Thread return code.
***************************************************************************/
STATIC DWORD waveThread(LPVOID lpParameter)
{
PWAVEALLOC pClient;
BOOL Terminate;
// DWORD dwThread; // garbage
Terminate = FALSE;
pClient = (PWAVEALLOC)lpParameter;
//
// Set our thread to high priority so we don't fail to pass
// new buffers to the device
//
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
//
// We start by waiting for something signalling that we've started
// and waiting for something to do.
//
SetEvent(pClient->AuxEvent2);
WaitForSingleObject(pClient->AuxEvent1, INFINITE);
//
// Now we're going
//
for (;;) {
WinAssert(pClient->hDev != INVALID_HANDLE_VALUE);
//
// Decode function number to perform
//
switch (pClient->AuxFunction) {
case WaveThreadAddBuffer:
//
// Intialize bytes recorded
//
if (pClient->DeviceType == WAVE_IN) {
pClient->AuxParam.pHdr->dwBytesRecorded = 0;
}
//
// Add the buffer to our list
//
{
LPWAVEHDR *pHdrSearch;
pClient->AuxParam.pHdr->lpNext = NULL;
pHdrSearch = &pClient->DeviceQueue;
while (*pHdrSearch) {
pHdrSearch = &(*pHdrSearch)->lpNext;
}
*pHdrSearch = pClient->AuxParam.pHdr;
}
// {
// PRETOUCHTHREADPARM * pttp;
//
// pttp = LocalAlloc(LMEM_FIXED,8);
//
// if (pttp!=NULL) {
// pttp->Addr = pClient->AuxParam.pHdr->lpData;
// pttp->Len = pClient->AuxParam.pHdr->dwBufferLength;
// CreateThread(NULL, 0, PreToucher, pttp, 0, &dwThread);
// }
// }
// Would need to declutter the system by WAITing for dead threads at some point???
//
// See if we can send more to the driver
//
if (pClient->NextBuffer == NULL) {
pClient->NextBuffer = pClient->AuxParam.pHdr;
pClient->BufferPosition = 0;
}
// /* Before we waveStart, let's just try to pre-touch that which we will soon want */
// {
// PWAVEHDR pHdr = pClient->NextBuffer;
// DWORD dwTick = GetTickCount();
// PreTouch( (PBYTE)pHdr->lpData + pClient->BufferPosition
// , pHdr->dwBufferLength - pClient->BufferPosition
// , TRUE
// );
// dprintf(("pre-touched out to limit. Took %d mSec", GetTickCount()-dwTick));
// }
pClient->AuxReturnCode = waveStart(pClient);
break;
case WaveThreadSetState:
//
// We have to make sure at least ONE buffer gets
// completed if we're doing input and it's input.
//
//
// Set Device state. By issuing state changes on THIS
// thread the calling thread can be sure that all Apc's
// generated by buffer completions will complete
// BEFORE this function completes.
//
pClient->AuxReturnCode =
waveSetState(pClient, pClient->AuxParam.State);
//
// Free the rest of our buffers if we're resetting
//
if (pClient->AuxParam.State == WAVE_DD_RESET) {
//
// Cancel any loops
//
pClient->LoopHead = NULL;
//
// This function must ALWAYS succeed
// Note that waveSetState closes the device on failure
//
pClient->AuxReturnCode = MMSYSERR_NOERROR;
//
// Check this worked (even if the driver's OK the
// IO subsystem can fail)
//
WinAssert(pClient->BytesOutstanding == 0);
//
// Free all buffers
//
waveFreeQ(pClient);
} else {
if (pClient->DeviceType == WAVE_IN &&
pClient->AuxReturnCode == MMSYSERR_NOERROR) {
if (pClient->AuxParam.State == WAVE_DD_STOP) {
//
// We're sort of stuck - we want to complete this
// buffer but we've got it tied up in the device
// We'll reset it here although this erroneously
// sets the position to 0
//
if (pClient->DeviceQueue) {
while (!(pClient->DeviceQueue->dwFlags & WHDR_COMPLETE) &&
pClient->BytesOutstanding != 0) {
waveSetState(pClient, WAVE_DD_RECORD);
pClient->AuxReturnCode =
waveSetState(pClient, WAVE_DD_STOP);
if (pClient->AuxReturnCode != MMSYSERR_NOERROR) {
break;
}
}
if (pClient->AuxReturnCode == MMSYSERR_NOERROR) {
pClient->DeviceQueue->dwFlags |= WHDR_COMPLETE;
//
// Tidy up next buffer
//
if (pClient->NextBuffer ==
pClient->DeviceQueue) {
pClient->NextBuffer =
pClient->DeviceQueue->lpNext;
pClient->BufferPosition = 0;
}
}
}
} else {
//
// If recording restore some buffers if necessary
//
if (pClient->AuxParam.State == WAVE_DD_RECORD) {
pClient->AuxReturnCode = waveStart(pClient);
}
}
}
}
break;
case WaveThreadGetData:
{
pClient->AuxReturnCode =
sndGetHandleData(pClient->hDev,
pClient->AuxParam.GetSetData.DataLen,
pClient->AuxParam.GetSetData.pData,
pClient->AuxParam.GetSetData.Function,
pClient->Event);
}
break;
case WaveThreadSetData:
{
pClient->AuxReturnCode =
sndSetHandleData(pClient->hDev,
pClient->AuxParam.GetSetData.DataLen,
pClient->AuxParam.GetSetData.pData,
pClient->AuxParam.GetSetData.Function,
pClient->Event);
}
break;
case WaveThreadBreakLoop:
if (pClient->LoopHead) {
//
// If we're in a loop then exit the loop at the
// end of the next iteration.
//
pClient->LoopCount = 0;
}
pClient->AuxReturnCode = MMSYSERR_NOERROR;
break;
case WaveThreadClose:
//
// Try to complete.
// If we're completed all our buffers then we can.
// otherwise we can't
//
if (pClient->DeviceQueue == NULL) {
pClient->AuxReturnCode = MMSYSERR_NOERROR;
} else {
pClient->AuxReturnCode = WAVERR_STILLPLAYING;
}
break;
case WaveThreadTerminate:
Terminate = TRUE;
break;
default:
WinAssert(FALSE); // Invalid call
break;
}
//
// Trap invalid callers
//
pClient->AuxFunction = WaveThreadInvalid;
//
// See if any Apcs need completing
//
waveCompleteBuffers(pClient);
//
// Complete ? - don't set the event here.
//
if (Terminate) {
return 1;
}
//
// Release the thread caller
//
SetEvent(pClient->AuxEvent2);
//
// Wait for more !
//
while (WaitForSingleObjectEx(pClient->AuxEvent1, INFINITE, TRUE) ==
WAIT_IO_COMPLETION) {
waveCompleteBuffers(pClient);
}
}
return 1; // Satisfy the compiler !
}
/****************************************************************************
* @doc INTERNAL
*
* @api void | waveCallback | This calls DriverCallback for a WAVEHDR.
*
* @parm PWAVEALLOC | pWave | Pointer to wave device.
*
* @parm DWORD | msg | The message.
*
* @parm DWORD | dw1 | message DWORD (dw2 is always set to 0).
*
* @rdesc There is no return value.
***************************************************************************/
void waveCallback(PWAVEALLOC pWave, DWORD msg, DWORD_PTR dw1)
{
// invoke the callback function, if it exists. dwFlags contains
// wave driver specific flags in the LOWORD and generic driver
// flags in the HIWORD
if (pWave->dwCallback)
DriverCallback(pWave->dwCallback, // user's callback DWORD
HIWORD(pWave->dwFlags), // callback flags
(HDRVR)pWave->hWave, // handle to the wave device
msg, // the message
pWave->dwInstance, // user's instance data
dw1, // first DWORD
0L); // second DWORD
}
/****************************************************************************
This function conforms to the standard Wave input driver message proc
(widMessage), which is documented in mmddk.d.
****************************************************************************/
DWORD APIENTRY widMessage(DWORD id, DWORD msg, DWORD_PTR dwUser, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
PWAVEALLOC pInClient;
MMRESULT mRet;
switch (msg) {
case WIDM_GETNUMDEVS:
D2(("WIDM_GETNUMDEVS"));
return sndGetNumDevs(WAVE_IN);
case WIDM_GETDEVCAPS:
D2(("WIDM_GETDEVCAPS"));
return waveGetDevCaps(id, WAVE_IN, (LPBYTE)dwParam1,
(DWORD)dwParam2);
case WIDM_OPEN:
D2(("WIDM_OPEN, device id==%d", id));
return waveOpen(WAVE_IN, id, dwUser, dwParam1, dwParam2);
case WIDM_CLOSE:
D2(("WIDM_CLOSE, device id==%d", id));
pInClient = (PWAVEALLOC)dwUser;
//
// Call our task to see if it's ready to complete
//
mRet = waveThreadCall(WaveThreadClose, pInClient);
if (mRet != MMSYSERR_NOERROR) {
return mRet;
}
waveCallback(pInClient, WIM_CLOSE, 0L);
//
// Close our device
//
if (pInClient->hDev != INVALID_HANDLE_VALUE) {
CloseHandle(pInClient->hDev);
}
EnterCriticalSection(&mmDrvCritSec);
/* We must set the status to 0 otherwise this thread
will never be used again if it the
WAVEALLOC_STATUS_LOWPRIORITY flag was set.
*/
pInClient->dwStatus = 0;
/* This makes this device free
*/
pInClient->hDev = INVALID_HANDLE_VALUE;
LeaveCriticalSection(&mmDrvCritSec);
return MMSYSERR_NOERROR;
case WIDM_ADDBUFFER:
D2(("WIDM_ADDBUFFER, device id==%d", id));
WinAssert(dwParam1 != 0);
WinAssert(!(((LPWAVEHDR)dwParam1)->dwFlags & ~(WHDR_INQUEUE|WHDR_DONE|WHDR_PREPARED|WHDR_BEGINLOOP|WHDR_ENDLOOP)));
((LPWAVEHDR)dwParam1)->dwFlags &= (WHDR_INQUEUE|WHDR_DONE|WHDR_PREPARED);
WinAssert(((LPWAVEHDR)dwParam1)->dwFlags & WHDR_PREPARED);
// check if it's been prepared
if (!(((LPWAVEHDR)dwParam1)->dwFlags & WHDR_PREPARED))
return WAVERR_UNPREPARED;
WinAssert(!(((LPWAVEHDR)dwParam1)->dwFlags & WHDR_INQUEUE));
// if it is already in our Q, then we cannot do this
if ( ((LPWAVEHDR)dwParam1)->dwFlags & WHDR_INQUEUE )
return ( WAVERR_STILLPLAYING );
// store the pointer to my WAVEALLOC structure in the wavehdr
pInClient = (PWAVEALLOC)dwUser;
((LPWAVEHDR)dwParam1)->reserved = (DWORD_PTR)(LPSTR)pInClient;
return waveWrite((LPWAVEHDR)dwParam1, pInClient);
case WIDM_STOP:
D2(("WIDM_STOP, device id==%d", id));
pInClient = (PWAVEALLOC)dwUser;
pInClient->AuxParam.State = WAVE_DD_STOP;
return waveThreadCall(WaveThreadSetState, pInClient);
case WIDM_START:
D2(("WIDM_START, device id==%d", id));
pInClient = (PWAVEALLOC)dwUser;
pInClient->AuxParam.State = WAVE_DD_RECORD;
return waveThreadCall(WaveThreadSetState, pInClient);
case WIDM_RESET:
D2(("WIDM_RESET, device id==%d", id));
pInClient = (PWAVEALLOC)dwUser;
pInClient->AuxParam.State = WAVE_DD_RESET;
return waveThreadCall(WaveThreadSetState, pInClient);
case WIDM_GETPOS:
D2(("WIDM_GETPOS"));
pInClient = (PWAVEALLOC)dwUser;
return waveGetPos(pInClient, (LPMMTIME)dwParam1, (DWORD)dwParam2);
/*
** Allow WOW version of WIDM_LOWPRIORITY
*/
case WIDM_LOWPRIORITY:
case MAKELONG(WIDM_LOWPRIORITY, 0xFFFF):
D2(("WIDM_LOWPRIORITY, device id==%d", id));
pInClient = (PWAVEALLOC)dwUser;
pInClient->dwStatus |= WAVEALLOC_STATUS_LOWPRIORITY;
return sndSetHandleData(pInClient->hDev,
0,
NULL,
IOCTL_WAVE_SET_LOW_PRIORITY,
pInClient->Event);
default:
return MMSYSERR_NOTSUPPORTED;
}
//
// Should not get here
//
WinAssert(0);
return MMSYSERR_NOTSUPPORTED;
}
/****************************************************************************
This function conforms to the standard Wave output driver message proc
(wodMessage), which is documented in mmddk.h.
****************************************************************************/
DWORD APIENTRY wodMessage(DWORD id, DWORD msg, DWORD_PTR dwUser, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
PWAVEALLOC pOutClient;
MMRESULT mRet;
switch (msg) {
case WODM_GETNUMDEVS:
D2(("WODM_GETNUMDEVS"));
return sndGetNumDevs(WAVE_OUT);
case WODM_GETDEVCAPS:
D2(("WODM_GETDEVCAPS, device id==%d", id));
return waveGetDevCaps(id, WAVE_OUT, (LPBYTE)dwParam1,
(DWORD)dwParam2);
case WODM_OPEN:
D2(("WODM_OPEN, device id==%d", id));
return waveOpen(WAVE_OUT, id, dwUser, dwParam1, dwParam2);
case WODM_CLOSE:
D2(("WODM_CLOSE, device id==%d", id));
pOutClient = (PWAVEALLOC)dwUser;
//
// Call our task to see if it's ready to complete
//
mRet = waveThreadCall(WaveThreadClose, pOutClient);
if (mRet != MMSYSERR_NOERROR) {
return mRet;
}
waveCallback(pOutClient, WOM_CLOSE, 0L);
//
// Close our device
//
if (pOutClient->hDev != INVALID_HANDLE_VALUE) {
CloseHandle(pOutClient->hDev);
EnterCriticalSection(&mmDrvCritSec);
pOutClient->hDev = INVALID_HANDLE_VALUE;
LeaveCriticalSection(&mmDrvCritSec);
}
return MMSYSERR_NOERROR;
case WODM_WRITE:
D3(("WODM_WRITE, device id==%d", id));
WinAssert(dwParam1 != 0);
WinAssert(!(((LPWAVEHDR)dwParam1)->dwFlags &
~(WHDR_INQUEUE|WHDR_DONE|WHDR_PREPARED|
WHDR_BEGINLOOP|WHDR_ENDLOOP)));
((LPWAVEHDR)dwParam1)->dwFlags &=
(WHDR_INQUEUE|WHDR_DONE|WHDR_PREPARED|
WHDR_BEGINLOOP|WHDR_ENDLOOP);
WinAssert(((LPWAVEHDR)dwParam1)->dwFlags & WHDR_PREPARED);
// check if it's been prepared
if (!(((LPWAVEHDR)dwParam1)->dwFlags & WHDR_PREPARED))
return WAVERR_UNPREPARED;
WinAssert(!(((LPWAVEHDR)dwParam1)->dwFlags & WHDR_INQUEUE));
// if it is already in our Q, then we cannot do this
if ( ((LPWAVEHDR)dwParam1)->dwFlags & WHDR_INQUEUE )
return ( WAVERR_STILLPLAYING );
// store the pointer to my WAVEALLOC structure in the wavehdr
pOutClient = (PWAVEALLOC)dwUser;
((LPWAVEHDR)dwParam1)->reserved = (DWORD_PTR)(LPSTR)pOutClient;
return waveWrite((LPWAVEHDR)dwParam1, pOutClient);
case WODM_PAUSE:
D2(("WODM_PAUSE, device id==%d", id));
pOutClient = (PWAVEALLOC)dwUser;
pOutClient->AuxParam.State = WAVE_DD_STOP;
return waveThreadCall(WaveThreadSetState, pOutClient);
case WODM_RESTART:
D2(("WODM_RESTART, device id==%d", id));
pOutClient = (PWAVEALLOC)dwUser;
pOutClient->AuxParam.State = WAVE_DD_PLAY;
return waveThreadCall(WaveThreadSetState, pOutClient);
case WODM_RESET:
D2(("WODM_RESET, device id==%d", id));
pOutClient = (PWAVEALLOC)dwUser;
pOutClient->AuxParam.State = WAVE_DD_RESET;
return waveThreadCall(WaveThreadSetState, pOutClient);
case WODM_BREAKLOOP:
pOutClient = (PWAVEALLOC)dwUser;
D2(("WODM_BREAKLOOP, device id==%d", id));
return waveThreadCall(WaveThreadBreakLoop, pOutClient);
case WODM_GETPOS:
D2(("WODM_GETPOS, device id==%d", id));
pOutClient = (PWAVEALLOC)dwUser;
return waveGetPos(pOutClient, (LPMMTIME)dwParam1, (DWORD)dwParam2);
case WODM_SETPITCH:
D2(("WODM_SETPITCH, device id==%d", id));
pOutClient = (PWAVEALLOC)dwUser;
pOutClient->AuxParam.GetSetData.pData = (PBYTE)&dwParam1;
pOutClient->AuxParam.GetSetData.DataLen = sizeof(DWORD);
pOutClient->AuxParam.GetSetData.Function = IOCTL_WAVE_SET_PITCH;
return waveThreadCall(WaveThreadSetData, pOutClient);
case WODM_SETVOLUME:
D2(("WODM_SETVOLUME, device id==%d", id));
//pOutClient = (PWAVEALLOC)dwUser;
//pOutClient->AuxParam.GetSetData.pData = *(PBYTE *)&dwParam1;
//pOutClient->AuxParam.GetSetData.DataLen = sizeof(DWORD);
//pOutClient->AuxParam.GetSetData.Function = IOCTL_WAVE_SET_VOLUME;
//return waveThreadCall(WaveThreadSetData, pOutClient);
{
//
// Translate to device volume structure
//
WAVE_DD_VOLUME Volume;
Volume.Left = LOWORD(dwParam1) << 16;
Volume.Right = HIWORD(dwParam1) << 16;
return sndSetData(WAVE_OUT, id, sizeof(Volume),
(PBYTE)&Volume, IOCTL_WAVE_SET_VOLUME);
}
case WODM_SETPLAYBACKRATE:
D2(("WODM_SETPLAYBACKRATE, device id==%d", id));
pOutClient = (PWAVEALLOC)dwUser;
pOutClient->AuxParam.GetSetData.pData = (PBYTE)&dwParam1;
pOutClient->AuxParam.GetSetData.DataLen = sizeof(DWORD);
pOutClient->AuxParam.GetSetData.Function =
IOCTL_WAVE_SET_PLAYBACK_RATE;
return waveThreadCall(WaveThreadSetData, pOutClient);
case WODM_GETPITCH:
D2(("WODM_GETPITCH, device id==%d", id));
pOutClient = (PWAVEALLOC)dwUser;
pOutClient->AuxParam.GetSetData.pData = (PBYTE)dwParam1;
pOutClient->AuxParam.GetSetData.DataLen = sizeof(DWORD);
pOutClient->AuxParam.GetSetData.Function = IOCTL_WAVE_GET_PITCH;
return waveThreadCall(WaveThreadGetData, pOutClient);
case WODM_GETVOLUME:
D2(("WODM_GETVOLUME, device id==%d", id));
//pOutClient = (PWAVEALLOC)dwUser;
//pOutClient->AuxParam.GetSetData.pData = *(PBYTE *)&dwParam1;
//pOutClient->AuxParam.GetSetData.DataLen = sizeof(DWORD);
//pOutClient->AuxParam.GetSetData.Function = IOCTL_WAVE_GET_VOLUME;
//return waveThreadCall(WaveThreadGetData, pOutClient);
{
//
// Translate to device volume structure
//
WAVE_DD_VOLUME Volume;
DWORD rc;
rc = sndGetData(WAVE_OUT, id, sizeof(Volume),
(PBYTE)&Volume, IOCTL_WAVE_GET_VOLUME);
if (rc == MMSYSERR_NOERROR) {
*(LPDWORD)dwParam1 =
(DWORD)MAKELONG(HIWORD(Volume.Left),
HIWORD(Volume.Right));
}
return rc;
}
case WODM_GETPLAYBACKRATE:
D2(("WODM_GETPLAYBACKRATE, device id==%d", id));
pOutClient = (PWAVEALLOC)dwUser;
pOutClient->AuxParam.GetSetData.pData = (PBYTE)dwParam1;
pOutClient->AuxParam.GetSetData.DataLen = sizeof(DWORD);
pOutClient->AuxParam.GetSetData.Function =
IOCTL_WAVE_GET_PLAYBACK_RATE;
return waveThreadCall(WaveThreadGetData, pOutClient);
default:
return MMSYSERR_NOTSUPPORTED;
}
//
// Should not get here
//
WinAssert(0);
return MMSYSERR_NOTSUPPORTED;
}