870 lines
26 KiB
C
870 lines
26 KiB
C
/****************************************************************************
|
|
*
|
|
* wavein.c
|
|
*
|
|
* WDM Audio support for Wave Input devices
|
|
*
|
|
* Copyright (C) Microsoft Corporation, 1997 - 1999 All Rights Reserved.
|
|
*
|
|
* History
|
|
* 5-12-97 - Noel Cross (NoelC)
|
|
*
|
|
***************************************************************************/
|
|
|
|
#include "wdmdrv.h"
|
|
|
|
#ifndef UNDER_NT
|
|
#pragma alloc_text(FIXCODE, waveCallback)
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
|
|
This function conforms to the standard Wave input driver message proc
|
|
(widMessage), which is documented in mmddk.d.
|
|
|
|
****************************************************************************/
|
|
DWORD FAR PASCAL _loadds widMessage
|
|
(
|
|
UINT id,
|
|
UINT msg,
|
|
DWORD_PTR dwUser,
|
|
DWORD_PTR dwParam1,
|
|
DWORD_PTR dwParam2
|
|
)
|
|
{
|
|
LPDEVICEINFO pInClient;
|
|
LPDEVICEINFO pDeviceInfo;
|
|
LPWAVEHDR lpWaveHdr;
|
|
MMRESULT mmr;
|
|
|
|
switch (msg)
|
|
{
|
|
case WIDM_INIT:
|
|
DPF(DL_TRACE|FA_WAVE, ("WIDM_INIT") );
|
|
return(wdmaudAddRemoveDevNode(WaveInDevice, (LPCWSTR)dwParam2, TRUE));
|
|
|
|
case DRVM_EXIT:
|
|
DPF(DL_TRACE|FA_WAVE, ("DRVM_EXIT WaveIn") );
|
|
return(wdmaudAddRemoveDevNode(WaveInDevice, (LPCWSTR)dwParam2, FALSE));
|
|
|
|
case WIDM_GETNUMDEVS:
|
|
DPF(DL_TRACE|FA_WAVE, ("WIDM_GETNUMDEVS") );
|
|
return wdmaudGetNumDevs(WaveInDevice, (LPCWSTR)dwParam1);
|
|
|
|
case WIDM_GETDEVCAPS:
|
|
DPF(DL_TRACE|FA_WAVE, ("WIDM_GETDEVCAPS") );
|
|
if (pDeviceInfo = GlobalAllocDeviceInfo((LPCWSTR)dwParam2))
|
|
{
|
|
pDeviceInfo->DeviceType = WaveInDevice;
|
|
pDeviceInfo->DeviceNumber = id;
|
|
mmr = wdmaudGetDevCaps(pDeviceInfo, (MDEVICECAPSEX FAR*)dwParam1);
|
|
GlobalFreeDeviceInfo(pDeviceInfo);
|
|
return mmr;
|
|
} else {
|
|
MMRRETURN( MMSYSERR_NOMEM );
|
|
}
|
|
|
|
case WIDM_PREFERRED:
|
|
DPF(DL_TRACE|FA_WAVE, ("WIDM_PREFERRED") );
|
|
return wdmaudSetPreferredDevice(
|
|
WaveInDevice,
|
|
id,
|
|
dwParam1,
|
|
dwParam2);
|
|
|
|
case WIDM_OPEN:
|
|
{
|
|
LPWAVEOPENDESC pwod = (LPWAVEOPENDESC)dwParam1;
|
|
|
|
if( (mmr=IsValidWaveOpenDesc(pwod)) != MMSYSERR_NOERROR )
|
|
{
|
|
MMRRETURN( mmr );
|
|
}
|
|
|
|
DPF(DL_TRACE|FA_WAVE, ("WIDM_OPEN") );
|
|
if (pDeviceInfo = GlobalAllocDeviceInfo((LPCWSTR)pwod->dnDevNode))
|
|
{
|
|
pDeviceInfo->DeviceType = WaveInDevice;
|
|
pDeviceInfo->DeviceNumber = id;
|
|
mmr = waveOpen(pDeviceInfo, dwUser, pwod, (DWORD)dwParam2);
|
|
GlobalFreeDeviceInfo(pDeviceInfo);
|
|
return mmr;
|
|
} else {
|
|
MMRRETURN( MMSYSERR_NOMEM );
|
|
}
|
|
}
|
|
|
|
case WIDM_CLOSE:
|
|
DPF(DL_TRACE|FA_WAVE, ("WIDM_CLOSE") );
|
|
pInClient = (LPDEVICEINFO)dwUser;
|
|
|
|
//
|
|
// At this point, we've committed to closing down this DeviceInfo.
|
|
// We mark the DeviceState as closing and hope for the best! If
|
|
// someone calls WIDM_ADDBUFFER while we're in this state, we've got
|
|
// problems!
|
|
//
|
|
if( ( (mmr=IsValidDeviceInfo(pInClient)) != MMSYSERR_NOERROR ) ||
|
|
( (mmr=IsValidDeviceState(pInClient->DeviceState,FALSE)) != MMSYSERR_NOERROR ) )
|
|
{
|
|
MMRRETURN( mmr );
|
|
}
|
|
|
|
mmr = wdmaudCloseDev(pInClient);
|
|
if (MMSYSERR_NOERROR == mmr)
|
|
{
|
|
waveCallback(pInClient, WIM_CLOSE, 0L);
|
|
|
|
ISVALIDDEVICEINFO(pInClient);
|
|
ISVALIDDEVICESTATE(pInClient->DeviceState,FALSE);
|
|
|
|
waveCleanUp(pInClient);
|
|
}
|
|
return mmr;
|
|
|
|
case WIDM_ADDBUFFER:
|
|
DPF(DL_TRACE|FA_WAVE, ("WIDM_ADDBUFFER") );
|
|
lpWaveHdr = (LPWAVEHDR)dwParam1;
|
|
pInClient = (LPDEVICEINFO)dwUser;
|
|
|
|
//
|
|
// Perform our asserts
|
|
//
|
|
if( ( (mmr=IsValidDeviceInfo(pInClient)) != MMSYSERR_NOERROR ) ||
|
|
( (mmr=IsValidDeviceState(pInClient->DeviceState,FALSE)) != MMSYSERR_NOERROR ) ||
|
|
( (mmr=IsValidWaveHeader(lpWaveHdr)) != MMSYSERR_NOERROR ) )
|
|
{
|
|
MMRRETURN( mmr );
|
|
}
|
|
|
|
// sanity check on the wavehdr
|
|
DPFASSERT(lpWaveHdr != NULL);
|
|
if (lpWaveHdr == NULL)
|
|
MMRRETURN( MMSYSERR_INVALPARAM );
|
|
|
|
// check if it's been prepared
|
|
DPFASSERT(lpWaveHdr->dwFlags & WHDR_PREPARED);
|
|
if (!(lpWaveHdr->dwFlags & WHDR_PREPARED))
|
|
MMRRETURN( WAVERR_UNPREPARED );
|
|
|
|
// if it is already in our Q, then we cannot do this
|
|
DPFASSERT(!(lpWaveHdr->dwFlags & WHDR_INQUEUE));
|
|
if ( lpWaveHdr->dwFlags & WHDR_INQUEUE )
|
|
MMRRETURN( WAVERR_STILLPLAYING );
|
|
//
|
|
// Put the request at the end of our queue.
|
|
//
|
|
return waveWrite(pInClient, lpWaveHdr);
|
|
|
|
case WIDM_STOP:
|
|
DPF(DL_TRACE|FA_WAVE, ("WIDM_STOP") );
|
|
pInClient = (LPDEVICEINFO)dwUser;
|
|
return wdmaudSetDeviceState(pInClient,
|
|
IOCTL_WDMAUD_WAVE_IN_STOP);
|
|
|
|
case WIDM_START:
|
|
DPF(DL_TRACE|FA_WAVE, ("WIDM_START") );
|
|
pInClient = (LPDEVICEINFO)dwUser;
|
|
return wdmaudSetDeviceState(pInClient,
|
|
IOCTL_WDMAUD_WAVE_IN_RECORD);
|
|
|
|
case WIDM_RESET:
|
|
DPF(DL_TRACE|FA_WAVE, ("WIDM_RESET") );
|
|
pInClient = (LPDEVICEINFO)dwUser;
|
|
return wdmaudSetDeviceState(pInClient,
|
|
IOCTL_WDMAUD_WAVE_IN_RESET);
|
|
|
|
case WIDM_GETPOS:
|
|
DPF(DL_TRACE|FA_WAVE, ("WIDM_GETPOS") );
|
|
pInClient = (LPDEVICEINFO)dwUser;
|
|
|
|
if( ( (mmr=IsValidDeviceInfo(pInClient)) != MMSYSERR_NOERROR) ||
|
|
( (mmr=IsValidDeviceState(pInClient->DeviceState,FALSE)) != MMSYSERR_NOERROR) )
|
|
{
|
|
MMRRETURN( mmr );
|
|
}
|
|
|
|
return wdmaudGetPos(pInClient,
|
|
(LPMMTIME)dwParam1,
|
|
(DWORD)dwParam2,
|
|
WaveInDevice);
|
|
|
|
#ifdef UNDER_NT
|
|
case WIDM_PREPARE:
|
|
DPF(DL_TRACE|FA_WAVE, ("WIDM_PREPARE") );
|
|
pInClient = (LPDEVICEINFO)dwUser;
|
|
|
|
if( ( (mmr=IsValidDeviceInfo(pInClient)) != MMSYSERR_NOERROR) ||
|
|
( (mmr=IsValidDeviceState(pInClient->DeviceState,FALSE)) != MMSYSERR_NOERROR) )
|
|
{
|
|
MMRRETURN( mmr );
|
|
}
|
|
|
|
return wdmaudPrepareWaveHeader(pInClient, (LPWAVEHDR)dwParam1);
|
|
|
|
case WIDM_UNPREPARE:
|
|
DPF(DL_TRACE|FA_WAVE, ("WIDM_UNPREPARE") );
|
|
pInClient = (LPDEVICEINFO)dwUser;
|
|
|
|
if( ( (mmr=IsValidDeviceInfo(pInClient)) != MMSYSERR_NOERROR) ||
|
|
( (mmr=IsValidDeviceState(pInClient->DeviceState,FALSE)) != MMSYSERR_NOERROR) )
|
|
{
|
|
MMRRETURN( mmr );
|
|
}
|
|
|
|
return wdmaudUnprepareWaveHeader(pInClient, (LPWAVEHDR)dwParam1);
|
|
#endif
|
|
|
|
default:
|
|
MMRRETURN( MMSYSERR_NOTSUPPORTED );
|
|
}
|
|
|
|
//
|
|
// Should not get here
|
|
//
|
|
|
|
DPFASSERT(0);
|
|
MMRRETURN( MMSYSERR_NOTSUPPORTED );
|
|
}
|
|
|
|
/****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @api void | waveCallback | This calls DriverCallback for a WAVEHDR.
|
|
*
|
|
* @parm LPDEVICEINFO | 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 FAR waveCallback
|
|
(
|
|
LPDEVICEINFO pWave,
|
|
UINT 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->DeviceHandle, // handle to the wave device
|
|
msg, // the message
|
|
pWave->dwInstance, // user's instance data
|
|
dw1, // first DWORD
|
|
0L); // second DWORD
|
|
}
|
|
|
|
/****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @api MMRESULT | waveOpen | Open wave device and set up logical device data
|
|
*
|
|
* @parm LPDEVICEINFO | DeviceInfo | Specifies if it's a wave input or output
|
|
* device
|
|
*
|
|
* @parm DWORD | dwUser | Input parameter to wodMessage - pointer to
|
|
* application's handle (generated by this routine)
|
|
*
|
|
* @parm LPWAVEOPENDESC | pwod | pointer to WAVEOPENDESC. Was dwParam1
|
|
* parameter to wodMessage
|
|
*
|
|
* @parm DWORD | dwParam2 | Input parameter to wodMessage
|
|
*
|
|
* @rdesc wodMessage return code.
|
|
***************************************************************************/
|
|
MMRESULT waveOpen
|
|
(
|
|
LPDEVICEINFO DeviceInfo,
|
|
DWORD_PTR dwUser,
|
|
LPWAVEOPENDESC pwod,
|
|
DWORD dwParam2
|
|
)
|
|
{
|
|
LPDEVICEINFO pClient; // pointer to client information structure
|
|
MMRESULT mmr;
|
|
#ifndef UNDER_NT
|
|
DWORD dwCallback16;
|
|
#endif
|
|
|
|
//
|
|
// allocate my per-client structure
|
|
//
|
|
pClient = GlobalAllocDeviceInfo(DeviceInfo->wstrDeviceInterface);
|
|
if (NULL == pClient)
|
|
{
|
|
MMRRETURN( MMSYSERR_NOMEM );
|
|
}
|
|
|
|
pClient->DeviceState = (LPVOID) GlobalAllocPtr( GPTR, sizeof( DEVICESTATE ) );
|
|
if (NULL == pClient->DeviceState)
|
|
{
|
|
GlobalFreeDeviceInfo( pClient );
|
|
MMRRETURN( MMSYSERR_NOMEM );
|
|
}
|
|
|
|
//
|
|
// Handle the query case and return early
|
|
//
|
|
if (WAVE_FORMAT_QUERY & dwParam2)
|
|
{
|
|
pClient->DeviceType = DeviceInfo->DeviceType;
|
|
pClient->DeviceNumber = DeviceInfo->DeviceNumber;
|
|
pClient->dwFlags = dwParam2;
|
|
|
|
mmr = wdmaudOpenDev( pClient, (LPWAVEFORMATEX)pwod->lpFormat );
|
|
|
|
if (mmr == MMSYSERR_NOTSUPPORTED)
|
|
{
|
|
mmr = WAVERR_BADFORMAT;
|
|
}
|
|
|
|
GlobalFreePtr( pClient->DeviceState );
|
|
GlobalFreeDeviceInfo( pClient );
|
|
return mmr;
|
|
}
|
|
|
|
#ifdef UNDER_NT
|
|
//
|
|
// Allocate memory for our critical section
|
|
//
|
|
pClient->DeviceState->csQueue = (LPVOID) GlobalAllocPtr( GPTR, sizeof( CRITICAL_SECTION ) );
|
|
if (NULL == pClient->DeviceState->csQueue)
|
|
{
|
|
GlobalFreePtr( pClient->DeviceState );
|
|
GlobalFreeDeviceInfo( pClient );
|
|
MMRRETURN( MMSYSERR_NOMEM );
|
|
}
|
|
|
|
try
|
|
{
|
|
InitializeCriticalSection( (LPCRITICAL_SECTION)pClient->DeviceState->csQueue );
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
GlobalFreePtr( pClient->DeviceState->csQueue );
|
|
GlobalFreePtr( pClient->DeviceState );
|
|
GlobalFreeDeviceInfo( pClient );
|
|
MMRRETURN( MMSYSERR_NOMEM );
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// fill out context data
|
|
//
|
|
pClient->DeviceNumber= DeviceInfo->DeviceNumber;
|
|
pClient->DeviceType = DeviceInfo->DeviceType;
|
|
pClient->dwInstance = pwod->dwInstance;
|
|
pClient->dwCallback = pwod->dwCallback;
|
|
pClient->dwFlags = dwParam2;
|
|
#ifdef UNDER_NT
|
|
pClient->DeviceHandle= (HANDLE32)pwod->hWave;
|
|
#else
|
|
pClient->DeviceHandle= (HANDLE32)MAKELONG(pwod->hWave,0);
|
|
_asm
|
|
{
|
|
mov ax, offset WaveDeviceCallback
|
|
mov word ptr [dwCallback16], ax
|
|
mov ax, seg WaveDeviceCallback
|
|
mov word ptr [dwCallback16+2], ax
|
|
}
|
|
pClient->dwCallback16= dwCallback16;
|
|
#endif
|
|
|
|
|
|
//
|
|
// initialize the device state
|
|
//
|
|
pClient->DeviceState->lpWaveQueue = NULL;
|
|
pClient->DeviceState->fRunning = FALSE;
|
|
pClient->DeviceState->fExit = FALSE;
|
|
if (pClient->DeviceType == WaveOutDevice)
|
|
pClient->DeviceState->fPaused = FALSE;
|
|
else
|
|
pClient->DeviceState->fPaused = TRUE;
|
|
#ifdef DEBUG
|
|
pClient->DeviceState->dwSig = DEVICESTATE_SIGNATURE;
|
|
#endif
|
|
//
|
|
// See if we can open our device
|
|
//
|
|
mmr = wdmaudOpenDev( pClient, (LPWAVEFORMATEX)pwod->lpFormat );
|
|
|
|
if (mmr != MMSYSERR_NOERROR)
|
|
{
|
|
if (mmr == MMSYSERR_NOTSUPPORTED)
|
|
{
|
|
mmr = WAVERR_BADFORMAT;
|
|
}
|
|
|
|
#ifdef UNDER_NT
|
|
DeleteCriticalSection( (LPCRITICAL_SECTION)pClient->DeviceState->csQueue );
|
|
GlobalFreePtr( pClient->DeviceState->csQueue );
|
|
//
|
|
// explicitly clear these values! We don't want to ever see these set
|
|
// again!
|
|
//
|
|
pClient->DeviceState->csQueue=NULL;
|
|
#endif
|
|
GlobalFreePtr( pClient->DeviceState );
|
|
pClient->DeviceState=NULL;
|
|
GlobalFreeDeviceInfo( pClient );
|
|
pClient=NULL;
|
|
|
|
MMRRETURN( mmr );
|
|
}
|
|
|
|
//
|
|
// Add instance to chain of devices
|
|
//
|
|
EnterCriticalSection(&wdmaudCritSec);
|
|
pClient->Next = pWaveDeviceList;
|
|
pWaveDeviceList = pClient;
|
|
LeaveCriticalSection(&wdmaudCritSec);
|
|
|
|
//
|
|
// give the client my driver dw
|
|
//
|
|
{
|
|
LPDEVICEINFO FAR *pUserHandle;
|
|
|
|
pUserHandle = (LPDEVICEINFO FAR *)dwUser;
|
|
*pUserHandle = pClient;
|
|
}
|
|
|
|
//
|
|
// sent client his OPEN callback message
|
|
//
|
|
waveCallback(pClient, DeviceInfo->DeviceType == WaveOutDevice ? WOM_OPEN : WIM_OPEN, 0L);
|
|
|
|
return MMSYSERR_NOERROR;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @api void | waveCleanUp | Free resources for a wave device
|
|
*
|
|
* @parm LPWAVEALLOC | 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.
|
|
***************************************************************************/
|
|
VOID waveCleanUp
|
|
(
|
|
LPDEVICEINFO pClient
|
|
)
|
|
{
|
|
LPDEVICEINFO FAR *ppCur ;
|
|
|
|
//
|
|
// remove from device chain
|
|
//
|
|
EnterCriticalSection(&wdmaudCritSec);
|
|
for (ppCur = &pWaveDeviceList;
|
|
*ppCur != NULL;
|
|
ppCur = &(*ppCur)->Next)
|
|
{
|
|
if (*ppCur == pClient)
|
|
{
|
|
*ppCur = (*ppCur)->Next;
|
|
break;
|
|
}
|
|
}
|
|
LeaveCriticalSection(&wdmaudCritSec);
|
|
|
|
#ifdef UNDER_NT
|
|
DeleteCriticalSection( (LPCRITICAL_SECTION)pClient->DeviceState->csQueue );
|
|
GlobalFreePtr( pClient->DeviceState->csQueue );
|
|
#endif
|
|
#ifdef DEBUG
|
|
//
|
|
// In debug, let's set all the values in the DEVICESTATE structure to bad
|
|
// values.
|
|
//
|
|
pClient->DeviceState->cSampleBits=0xDEADBEEF;
|
|
pClient->DeviceState->hThread=NULL;
|
|
pClient->DeviceState->dwThreadId=0xDEADBEEF;
|
|
pClient->DeviceState->lpWaveQueue=NULL;
|
|
pClient->DeviceState->csQueue=NULL;
|
|
pClient->DeviceState->hevtQueue=NULL;
|
|
pClient->DeviceState->hevtExitThread=NULL;
|
|
|
|
#endif
|
|
GlobalFreePtr( pClient->DeviceState );
|
|
pClient->DeviceState=NULL;
|
|
|
|
#ifdef DEBUG
|
|
//
|
|
// Now set all the values in the DEVICEINFO structure to bad values.
|
|
//
|
|
// pClient->Next=(LPDEVICEINFO)0xDEADBEEF;
|
|
pClient->DeviceNumber=-1;
|
|
pClient->DeviceType=0xDEADBEEF;
|
|
pClient->DeviceHandle=NULL;
|
|
pClient->dwInstance=(DWORD_PTR)NULL;
|
|
pClient->dwCallback=(DWORD_PTR)NULL;
|
|
pClient->dwCallback16=0xDEADBEEF;
|
|
pClient->dwFlags=0xDEADBEEF;
|
|
pClient->DataBuffer=NULL;
|
|
pClient->HardwareCallbackEventHandle=NULL;
|
|
pClient->dwCallbackType=0xDEADBEEF;
|
|
pClient->dwLineID=0xDEADBEEF;
|
|
pClient->dwFormat=0xDEADBEEF;
|
|
// pClient->DeviceState=(LPDEVICESTATE)0xDEADBEEF;
|
|
|
|
#endif
|
|
GlobalFreeDeviceInfo( pClient ) ;
|
|
pClient=NULL;
|
|
DPF(DL_TRACE|FA_WAVE,("DeviceState gone!") );
|
|
}
|
|
|
|
#ifdef UNDER_NT
|
|
/****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @api MMRESULT | wdmaudPrepareWaveHeader |
|
|
*
|
|
* @parm LPDEVICEINFO | DeviceInfo | The data associated with the logical wave
|
|
* device.
|
|
*
|
|
* @parm LPWAVEHDR | pHdr | Pointer to a wave buffer
|
|
*
|
|
* @rdesc A MMSYS... type return code for the application.
|
|
*
|
|
***************************************************************************/
|
|
MMRESULT wdmaudPrepareWaveHeader
|
|
(
|
|
LPDEVICEINFO DeviceInfo,
|
|
LPWAVEHDR pHdr
|
|
)
|
|
{
|
|
PWAVEPREPAREDATA pWavePrepareData;
|
|
|
|
DPFASSERT(pHdr);
|
|
|
|
pHdr->lpNext = NULL;
|
|
pHdr->reserved = (DWORD_PTR)NULL;
|
|
|
|
//
|
|
// Allocate memory for the prepared header instance data
|
|
//
|
|
pWavePrepareData = (PWAVEPREPAREDATA) GlobalAllocPtr( GPTR, sizeof(*pWavePrepareData));
|
|
if (pWavePrepareData == NULL)
|
|
{
|
|
MMRRETURN( MMSYSERR_NOMEM );
|
|
}
|
|
|
|
//
|
|
// Allocate memory for our overlapped structure
|
|
//
|
|
pWavePrepareData->pOverlapped =
|
|
(LPOVERLAPPED)HeapAlloc( GetProcessHeap(), 0, sizeof( OVERLAPPED ));
|
|
if (NULL == pWavePrepareData->pOverlapped)
|
|
{
|
|
GlobalFreePtr( pWavePrepareData );
|
|
MMRRETURN( MMSYSERR_NOMEM );
|
|
}
|
|
|
|
RtlZeroMemory( pWavePrepareData->pOverlapped, sizeof( OVERLAPPED ) );
|
|
|
|
//
|
|
// Initialize the event once per preparation
|
|
//
|
|
if (NULL == (pWavePrepareData->pOverlapped->hEvent =
|
|
CreateEvent( NULL, FALSE, FALSE, NULL )))
|
|
{
|
|
HeapFree( GetProcessHeap(), 0, pWavePrepareData->pOverlapped);
|
|
GlobalFreePtr( pWavePrepareData );
|
|
MMRRETURN( MMSYSERR_NOMEM );
|
|
}
|
|
#ifdef DEBUG
|
|
pWavePrepareData->dwSig=WAVEPREPAREDATA_SIGNATURE;
|
|
#endif
|
|
|
|
//
|
|
// This next line adds this info to the main header. Only after this point
|
|
// will the info be used.
|
|
//
|
|
pHdr->reserved = (DWORD_PTR)pWavePrepareData;
|
|
|
|
// Still have WinMM prepare this header
|
|
return( MMSYSERR_NOTSUPPORTED );
|
|
}
|
|
|
|
/****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @api MMRESULT | wdmaudUnprepareWaveHeader |
|
|
*
|
|
* @parm LPDEVICEINFO | DeviceInfo | The data associated with the logical wave
|
|
* device.
|
|
*
|
|
* @parm LPWAVEHDR | pHdr | Pointer to a wave buffer
|
|
*
|
|
* @rdesc A MMSYS... type return code for the application.
|
|
*
|
|
***************************************************************************/
|
|
MMRESULT wdmaudUnprepareWaveHeader
|
|
(
|
|
LPDEVICEINFO DeviceInfo,
|
|
LPWAVEHDR pHdr
|
|
)
|
|
{
|
|
MMRESULT mmr;
|
|
PWAVEPREPAREDATA pWavePrepareData;
|
|
|
|
if( ((mmr=IsValidWaveHeader(pHdr)) !=MMSYSERR_NOERROR ) ||
|
|
((mmr=IsValidPrepareWaveHeader((PWAVEPREPAREDATA)(pHdr->reserved))) != MMSYSERR_NOERROR ) )
|
|
{
|
|
MMRRETURN( mmr );
|
|
}
|
|
|
|
pWavePrepareData = (PWAVEPREPAREDATA)pHdr->reserved;
|
|
//
|
|
// This next line removes the WaveHeader from the list. It is no longer
|
|
// valid after this point!
|
|
//
|
|
pHdr->reserved = (DWORD_PTR)NULL;
|
|
|
|
CloseHandle( pWavePrepareData->pOverlapped->hEvent );
|
|
//
|
|
// When you free something, you should make sure that you trash it before
|
|
// the free. But, in this case, the only thing we use in the pOverlapped
|
|
// structure is the hEvent!
|
|
//
|
|
pWavePrepareData->pOverlapped->hEvent=NULL;
|
|
HeapFree( GetProcessHeap(), 0, pWavePrepareData->pOverlapped);
|
|
#ifdef DEBUG
|
|
pWavePrepareData->pOverlapped=NULL;
|
|
pWavePrepareData->dwSig=0;
|
|
#endif
|
|
GlobalFreePtr( pWavePrepareData );
|
|
|
|
// Still have WinMM prepare this header
|
|
return( MMSYSERR_NOTSUPPORTED );
|
|
}
|
|
|
|
#endif
|
|
/****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @api MMRESULT | waveWrite | This routine adds the header
|
|
* to the queue and then submits the buffer to the device
|
|
*
|
|
* @parm LPDEVICEINFO | DeviceInfo | The data associated with the logical wave
|
|
* device.
|
|
*
|
|
* @parm LPWAVEHDR | pHdr | Pointer to a wave buffer
|
|
*
|
|
* @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.
|
|
***************************************************************************/
|
|
MMRESULT waveWrite
|
|
(
|
|
LPDEVICEINFO DeviceInfo,
|
|
LPWAVEHDR pHdr
|
|
)
|
|
{
|
|
MMRESULT mmr;
|
|
LPWAVEHDR pTemp;
|
|
|
|
//
|
|
// Mark this buffer because kmixer doesn't handle the dwFlags
|
|
//
|
|
pHdr->dwFlags |= WHDR_INQUEUE;
|
|
pHdr->dwFlags &= ~WHDR_DONE;
|
|
|
|
#ifndef UNDER_NT
|
|
//
|
|
// Store the context for this write in the header so that
|
|
// we know which client to send this back to on completion.
|
|
//
|
|
pHdr->reserved = (DWORD)DeviceInfo;
|
|
#endif
|
|
|
|
CRITENTER ;
|
|
DPF(DL_MAX|FA_WAVE, ("(ECS)") ); // Enter critical section
|
|
|
|
if (!DeviceInfo->DeviceState->lpWaveQueue)
|
|
{
|
|
DeviceInfo->DeviceState->lpWaveQueue = pHdr;
|
|
pTemp = NULL;
|
|
|
|
#ifdef UNDER_NT
|
|
if( (DeviceInfo->DeviceState->hevtQueue) &&
|
|
(DeviceInfo->DeviceState->hevtQueue != (HANDLE)FOURTYTHREE) &&
|
|
(DeviceInfo->DeviceState->hevtQueue != (HANDLE)FOURTYTWO) )
|
|
{
|
|
//
|
|
// If we get here, waveThread is waiting on hevtQueue because lpWaveQueue
|
|
// was empty and the completion thread exists == hevtQueue exists.
|
|
// So,we want to signal the thread to wake up so we can have this
|
|
// header serviced. Note: the difference between this call and the
|
|
// call made in wdmaudDestroyCompletionThread is that we don't set
|
|
// fExit to TRUE before making the call.
|
|
//
|
|
DPF(DL_MAX|FA_WAVE, ("Setting DeviceInfo->hevtQueue"));
|
|
SetEvent( DeviceInfo->DeviceState->hevtQueue );
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
for (pTemp = DeviceInfo->DeviceState->lpWaveQueue;
|
|
pTemp->lpNext != NULL;
|
|
pTemp = pTemp->lpNext);
|
|
|
|
pTemp->lpNext = pHdr;
|
|
}
|
|
|
|
DPF(DL_MAX|FA_WAVE, ("(LCS)") ); // Leave critical section
|
|
CRITLEAVE ;
|
|
|
|
//
|
|
// Call the 16 or 32-bit routine to send the buffer down
|
|
// to the kernel
|
|
//
|
|
mmr = wdmaudSubmitWaveHeader(DeviceInfo, pHdr);
|
|
if (mmr != MMSYSERR_NOERROR)
|
|
{
|
|
// Unlink...
|
|
if (pTemp)
|
|
{
|
|
pTemp->lpNext = NULL;
|
|
} else {
|
|
DeviceInfo->DeviceState->lpWaveQueue = NULL;
|
|
}
|
|
pHdr->dwFlags &= ~WHDR_INQUEUE;
|
|
DPF(DL_WARNING|FA_WAVE,("wdmaudSubmitWaveHeader failed mmr=%08X", mmr) );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Kick start the device if it has been shutdown because of
|
|
// starvation. Also this allows waveOut to start when the
|
|
// first waveheader is submitted to the device.
|
|
//
|
|
if (!DeviceInfo->DeviceState->fRunning && !DeviceInfo->DeviceState->fPaused)
|
|
{
|
|
mmr = wdmaudSetDeviceState(DeviceInfo, (DeviceInfo->DeviceType == WaveOutDevice) ?
|
|
IOCTL_WDMAUD_WAVE_OUT_PLAY :
|
|
IOCTL_WDMAUD_WAVE_IN_RECORD);
|
|
if (mmr != MMSYSERR_NOERROR)
|
|
{
|
|
MMRESULT mmrError;
|
|
|
|
mmrError = wdmaudSetDeviceState(DeviceInfo, (DeviceInfo->DeviceType == WaveOutDevice) ?
|
|
IOCTL_WDMAUD_WAVE_OUT_RESET :
|
|
IOCTL_WDMAUD_WAVE_IN_RESET);
|
|
if (mmrError != MMSYSERR_NOERROR)
|
|
{
|
|
DPF(DL_WARNING|FA_WAVE, ("Couldn't reset device after error putting into run state"));
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
DPF(DL_MAX|FA_WAVE, ("DeviceInfo = x%08lx, fRunning = %d, fPaused = %d",
|
|
DeviceInfo,
|
|
DeviceInfo->DeviceState->fRunning,
|
|
DeviceInfo->DeviceState->fPaused) );
|
|
}
|
|
}
|
|
|
|
MMRRETURN( mmr );
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
* @doc INTERNAL
|
|
*
|
|
* @api VOID | waveCompleteHeader |
|
|
*
|
|
* @parm LPDEVICEINFO | DeviceInfo | The data associated with the logical wave
|
|
* device.
|
|
*
|
|
* @comm The buffer flags are set and the buffer is passed to the auxiliary
|
|
* device task for processing.
|
|
***************************************************************************/
|
|
VOID waveCompleteHeader
|
|
(
|
|
LPDEVICEINFO DeviceInfo
|
|
)
|
|
{
|
|
LPWAVEHDR pHdr;
|
|
MMRESULT mmr;
|
|
|
|
// NOTE: This routine is called from within the csQueue critical section!!!
|
|
//
|
|
// Only remove headers from the front of the queue so that order is maintained.
|
|
// Note that pHdr is the head, The DeviceInfo structure's DeviceState's
|
|
// lpWaveQueue pointer then gets updated to the next location.
|
|
//
|
|
|
|
//
|
|
// Never use bad data when handling this Completion!
|
|
//
|
|
if( (pHdr = DeviceInfo->DeviceState->lpWaveQueue) &&
|
|
( (mmr=IsValidWaveHeader(pHdr)) == MMSYSERR_NOERROR ) )
|
|
{
|
|
DeviceInfo->DeviceState->lpWaveQueue = DeviceInfo->DeviceState->lpWaveQueue->lpNext;
|
|
|
|
#ifdef UNDER_NT
|
|
//
|
|
// Free temporary DeviceInfo for the asynchronous i/o
|
|
//
|
|
{
|
|
PWAVEPREPAREDATA pWavePrepareData;
|
|
|
|
pWavePrepareData = (PWAVEPREPAREDATA)pHdr->reserved;
|
|
|
|
//
|
|
// Never attempt to free garbage!
|
|
//
|
|
if( (mmr=IsValidPrepareWaveHeader(pWavePrepareData)) == MMSYSERR_NOERROR )
|
|
GlobalFreePtr( pWavePrepareData->pdi );
|
|
}
|
|
// Invoke the callback function..
|
|
DPF(DL_TRACE|FA_WAVE, ("WaveHdr being returned: pHdr = 0x%08lx dwBytesRecorded = 0x%08lx",
|
|
pHdr, pHdr->dwBytesRecorded) );
|
|
|
|
pHdr->lpNext = NULL ;
|
|
pHdr->dwFlags &= ~WHDR_INQUEUE ;
|
|
pHdr->dwFlags |= WHDR_DONE ;
|
|
|
|
|
|
// NOTE: This callback is within the csQueue critical section !!!
|
|
|
|
waveCallback(DeviceInfo,
|
|
DeviceInfo->DeviceType == WaveOutDevice ? WOM_DONE : WIM_DATA,
|
|
(DWORD_PTR)pHdr);
|
|
|
|
DPF(DL_TRACE|FA_WAVE, ("Done") );
|
|
#else
|
|
pHdr->dwFlags &= ~WHDR_INQUEUE;
|
|
pHdr->dwFlags |= WHDR_DONE;
|
|
pHdr->lpNext = NULL;
|
|
|
|
waveCallback((LPDEVICEINFO)pHdr->reserved,
|
|
DeviceInfo->DeviceType == WaveOutDevice ? WOM_DONE : WIM_DATA,
|
|
(DWORD)pHdr);
|
|
#endif
|
|
|
|
}
|
|
}
|