|
|
/****************************************************************************
* * 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; }
|