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.
 
 
 
 
 
 

3602 lines
96 KiB

/***************************************************************************
*
* Copyright (C) 1995-2001 Microsoft Corporation. All Rights Reserved.
*
* File: emvad.cpp
* Content: Emulated Virtual Audio Device class
* "emvad.cpp" is a misnomer; it does contain CEmRenderDevice,
* the emulated (via the wave* API) audio device, but it also
* has the CEm*WaveBuffer classes, which represent software
* audio buffers that can be attached to *any* mixer device;
* that is, both to CEmRenderDevice and to CVxdRenderDevice.
* History:
* Date By Reason
* ==== == ======
* 1/1/97 dereks Created
* 1999-2001 duganp Fixes and updates
*
***************************************************************************/
#include "dsoundi.h"
/***************************************************************************
*
* CEmRenderDevice
*
* Description:
* Object constructor.
*
* Arguments:
* (void)
*
* Returns:
* (void)
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmRenderDevice::CEmRenderDevice"
CEmRenderDevice::CEmRenderDevice(void)
: CMxRenderDevice(VAD_DEVICETYPE_EMULATEDRENDER)
{
DPF_ENTER();
DPF_CONSTRUCT(CEmRenderDevice);
DPF_LEAVE_VOID();
}
/***************************************************************************
*
* ~CEmRenderDevice
*
* Description:
* Object destructor
*
* Arguments:
* (void)
*
* Returns:
* (void)
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmRenderDevice::~CEmRenderDevice"
CEmRenderDevice::~CEmRenderDevice(void)
{
DPF_ENTER();
DPF_DESTRUCT(CEmRenderDevice);
DPF_LEAVE_VOID();
}
/***************************************************************************
*
* EnumDrivers
*
* Description:
* Creates a list of driver GUIDs that can be used to initialize this
* device.
*
* Arguments:
* CList& [in/out]: pointer to a CList object that will be filled with
* CDeviceDescription objects. The caller is
* responsible for freeing these objects.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmRenderDevice::EnumDrivers"
HRESULT CEmRenderDevice::EnumDrivers(CObjectList<CDeviceDescription> *plstDrivers)
{
CDeviceDescription * pDesc = NULL;
LPTSTR pszInterface = NULL;
HRESULT hr = DS_OK;
TCHAR szTemplate[0x100];
TCHAR szEmulated[0x100];
TCHAR szName[0x400];
UINT cDevices;
BYTE bDeviceId;
WAVEOUTCAPS woc;
GUID guid;
MMRESULT mmr;
DPF_ENTER();
// LIMITATION: We can't support more than 0xFF emulated devices,
// because we pack the device ID into a byte member of a GUID.
cDevices = waveOutGetNumDevs();
cDevices = NUMERIC_CAST(cDevices, BYTE);
// Load string templates
if(!LoadString(hModule, IDS_DS_DRIVERLD, szTemplate, NUMELMS(szTemplate)))
{
DPF(DPFLVL_ERROR, "Can't load driver template string");
hr = DSERR_OUTOFMEMORY;
}
if(SUCCEEDED(hr) && !LoadString(hModule, IDS_EMULATED, szEmulated, NUMELMS(szEmulated)))
{
DPF(DPFLVL_ERROR, "Can't load emulated template string");
hr = DSERR_OUTOFMEMORY;
}
// Enumerate each waveOut device and add it to the list
for(bDeviceId = 0; bDeviceId < cDevices && SUCCEEDED(hr); bDeviceId++)
{
// Get the driver GUID
g_pVadMgr->GetDriverGuid(m_vdtDeviceType, bDeviceId, &guid);
// Create the device description object
pDesc = NEW(CDeviceDescription(m_vdtDeviceType, guid, bDeviceId));
hr = HRFROMP(pDesc);
// Get the device name
if(SUCCEEDED(hr))
{
mmr = waveOutGetDevCaps(bDeviceId, &woc, sizeof(woc));
hr = MMRESULTtoHRESULT(mmr);
}
if(SUCCEEDED(hr))
{
lstrcpy(szName, woc.szPname);
lstrcat(szName, szEmulated);
}
if(SUCCEEDED(hr))
{
pDesc->m_strName = szName;
}
// Get the device path
if(SUCCEEDED(hr))
{
wsprintf(szName, szTemplate, bDeviceId);
pDesc->m_strPath = szName;
}
// Get the device interface
if(SUCCEEDED(hr))
{
GetWaveDeviceInterface(bDeviceId, FALSE, &pszInterface);
pDesc->m_strInterface = pszInterface;
}
// Get the device devnode
if(SUCCEEDED(hr))
{
GetWaveDeviceDevnode(bDeviceId, FALSE, &pDesc->m_dwDevnode);
}
// Add the driver to the list
if(SUCCEEDED(hr))
{
hr = HRFROMP(plstDrivers->AddNodeToList(pDesc));
}
// Clean up
MEMFREE(pszInterface);
RELEASE(pDesc);
}
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* Initialize
*
* Description:
* Initializes the device. If this function fails, the object should
* be immediately deleted.
*
* Arguments:
* CDeviceDescription * [in]: driver description.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmRenderDevice::Initialize"
HRESULT CEmRenderDevice::Initialize(CDeviceDescription *pDesc)
{
LPWAVEFORMATEX pwfxFormat = NULL;
HRESULT hr;
DPF_ENTER();
// Initialize the base class
hr = CMxRenderDevice::Initialize(pDesc);
// Allocate the default format
if(SUCCEEDED(hr))
{
pwfxFormat = AllocDefWfx();
hr = HRFROMP(pwfxFormat);
}
// Create the mixer
if(SUCCEEDED(hr))
{
if(!EnumStandardFormats(pwfxFormat, pwfxFormat))
{
// If none of the formats worked, the device is probably allocated
hr = DSERR_ALLOCATED;
}
}
// Clean up
MEMFREE(pwfxFormat);
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* GetCaps
*
* Description:
* Fills a DSCAPS structure with the capabilities of the device.
*
* Arguments:
* LPDSCAPS [out]: receives caps.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmRenderDevice::GetCaps"
HRESULT CEmRenderDevice::GetCaps(LPDSCAPS pCaps)
{
HRESULT hr = DS_OK;
WAVEOUTCAPS woc;
MMRESULT mmr;
DPF_ENTER();
ASSERT(sizeof(*pCaps) == pCaps->dwSize);
// Query the waveOut device
mmr = waveOutGetDevCaps(m_pDeviceDescription->m_uWaveDeviceId, &woc, sizeof(woc));
hr = MMRESULTtoHRESULT(mmr);
if(SUCCEEDED(hr))
{
ZeroMemoryOffset(pCaps, pCaps->dwSize, sizeof(pCaps->dwSize));
if(woc.dwFormats & WAVE_FORMAT_1M08 || woc.dwFormats & WAVE_FORMAT_2M08 || woc.dwFormats & WAVE_FORMAT_4M08)
{
pCaps->dwFlags |= DSCAPS_PRIMARYMONO | DSCAPS_PRIMARY8BIT;
}
if(woc.dwFormats & WAVE_FORMAT_1S08 || woc.dwFormats & WAVE_FORMAT_2S08 || woc.dwFormats & WAVE_FORMAT_4S08)
{
pCaps->dwFlags |= DSCAPS_PRIMARYSTEREO | DSCAPS_PRIMARY8BIT;
}
if(woc.dwFormats & WAVE_FORMAT_1M16 || woc.dwFormats & WAVE_FORMAT_2M16 || woc.dwFormats & WAVE_FORMAT_4M16)
{
pCaps->dwFlags |= DSCAPS_PRIMARYMONO | DSCAPS_PRIMARY16BIT;
}
if(woc.dwFormats & WAVE_FORMAT_1S16 || woc.dwFormats & WAVE_FORMAT_2S16 || woc.dwFormats & WAVE_FORMAT_4S16)
{
pCaps->dwFlags |= DSCAPS_PRIMARYSTEREO | DSCAPS_PRIMARY16BIT;
}
pCaps->dwFlags |= DSCAPS_EMULDRIVER;
pCaps->dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
pCaps->dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
}
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* CreatePrimaryBuffer
*
* Description:
* Creates a primary buffer object.
*
* Arguments:
* DWORD [in]: buffer flags.
* LPVOID [in]: buffer instace identifier.
* CPrimaryRenderWaveBuffer ** [out]: receives pointer to primary buffer.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmRenderDevice::CreatePrimaryBuffer"
HRESULT CEmRenderDevice::CreatePrimaryBuffer(DWORD dwFlags, LPVOID pvInstance, CPrimaryRenderWaveBuffer **ppBuffer)
{
CEmPrimaryRenderWaveBuffer * pBuffer = NULL;
HRESULT hr = DS_OK;
DPF_ENTER();
// Create a new primary buffer wrapper object
pBuffer = NEW(CEmPrimaryRenderWaveBuffer(this, pvInstance));
hr = HRFROMP(pBuffer);
if(SUCCEEDED(hr))
{
hr = pBuffer->Initialize(dwFlags);
}
if(SUCCEEDED(hr))
{
*ppBuffer = pBuffer;
}
else
{
RELEASE(pBuffer);
}
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* LockMixerDestination
*
* Description:
* Locks the mixer destination for writes.
*
* Arguments:
* DWORD [in]: starting position.
* DWORD [in]: amount to lock.
* LPVOID * [out]: receives first lock pointer.
* LPDWORD [out]: receives first lock size.
* LPVOID * [out]: receives second lock pointer.
* LPDWORD [out]: receives second lock size.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmRenderDevice::LockMixerDestination"
HRESULT CEmRenderDevice::LockMixerDestination(DWORD ibLock, DWORD cbLock, LPVOID *ppvLock1, LPDWORD pcbLock1, LPVOID *ppvLock2, LPDWORD pcbLock2)
{
CWeGrDest * pWeGrDest = (CWeGrDest *)m_pMixDest;
HRESULT hr;
DPF_ENTER();
hr = pWeGrDest->Lock(ppvLock1, (int *)pcbLock1, ppvLock2, (int *)pcbLock2, ibLock, min(cbLock, (DWORD)pWeGrDest->m_cbBuffer));
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* UnlockMixerDestination
*
* Description:
* Unlocks the mixer destination for writes.
*
* Arguments:
* LPVOID [in]: first lock pointer.
* DWORD [in]: first lock size.
* LPVOID [in]: second lock pointer.
* DWORD [in]: second lock size.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmRenderDevice::UnlockMixerDestination"
HRESULT CEmRenderDevice::UnlockMixerDestination(LPVOID pvLock1, DWORD cbLock1, LPVOID pvLock2, DWORD cbLock2)
{
CWeGrDest * pWeGrDest = (CWeGrDest *)m_pMixDest;
HRESULT hr;
DPF_ENTER();
hr = pWeGrDest->Unlock(pvLock1, cbLock1, pvLock2, cbLock2);
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* EnumStandardFormatsCallback
*
* Description:
* Callback function for EnumStandardFormats called from Initialize.
*
* Arguments:
* LPWAVEFORMATEX [in]: format.
*
* Returns:
* BOOL: TRUE to continue enumerating.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmRenderDevice::EnumStandardFormatsCallback"
BOOL CEmRenderDevice::EnumStandardFormatsCallback(LPCWAVEFORMATEX pwfx)
{
CWeGrDest * pMixDest;
HRESULT hr;
DPF_ENTER();
// Create the mixer destination
pMixDest = NEW(CWeGrDest(m_pDeviceDescription->m_uWaveDeviceId));
hr = HRFROMP(pMixDest);
// Attempt to create the mixer
if SUCCEEDED(hr)
{
hr = CreateMixer(pMixDest, pwfx);
}
// Clean up after failures
if (FAILED(hr))
{
// If we failed to create the mixer, then clean up the pMixDest.
// we don't have to free the mixer; if CreateMixer had succeeded
// we wouldn't be in this if-block.
// We don't have to free the pMixDest, because if CreateMixer fails
// it frees pMixDest. This is a bit messy. The object that
// allocates the resource should be the one to free it.
if (pMixDest)
{
pMixDest = NULL;
}
}
DPF_LEAVE(FAILED(hr));
return FAILED(hr);
}
/***************************************************************************
*
* CEmPrimaryRenderWaveBuffer
*
* Description:
* Emulated device primary wave buffer constructor.
*
* Arguments:
* CEmRenderDevice * [in]: parent device.
* LPVOID [in]: instance identifier.
*
* Returns:
* (void)
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmPrimaryRenderWaveBuffer::CEmPrimaryRenderWaveBuffer"
CEmPrimaryRenderWaveBuffer::CEmPrimaryRenderWaveBuffer(CEmRenderDevice *pEmDevice, LPVOID pvInstance)
: CPrimaryRenderWaveBuffer(pEmDevice, pvInstance)
{
DPF_ENTER();
DPF_CONSTRUCT(CEmPrimaryRenderWaveBuffer);
// Initialize defaults
m_pEmDevice = pEmDevice;
DPF_LEAVE_VOID();
}
/***************************************************************************
*
* ~CEmPrimaryRenderWaveBuffer
*
* Description:
* Emulated device primary wave buffer destructor.
*
* Arguments:
* (void)
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmPrimaryRenderWaveBuffer::~CEmPrimaryRenderWaveBuffer"
CEmPrimaryRenderWaveBuffer::~CEmPrimaryRenderWaveBuffer(void)
{
DPF_ENTER();
DPF_DESTRUCT(CEmPrimaryRenderWaveBuffer);
DPF_LEAVE_VOID();
}
/***************************************************************************
*
* Initialize
*
* Description:
* Initializes the object.
*
* Arguments:
* DWORD [in]: flags.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmPrimaryRenderWaveBuffer::Initialize"
HRESULT CEmPrimaryRenderWaveBuffer::Initialize(DWORD dwFlags)
{
VADRBUFFERDESC vrbd;
HRESULT hr;
DPF_ENTER();
ZeroMemory(&vrbd, sizeof(vrbd));
vrbd.dwFlags = dwFlags | DSBCAPS_LOCSOFTWARE;
hr = CPrimaryRenderWaveBuffer::Initialize(&vrbd, NULL);
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* GetCaps
*
* Description:
* Gets capabilities for the device.
*
* Arguments:
* LPVADRBUFFERCAPS [out]: receives caps.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmPrimaryRenderWaveBuffer::GetCaps"
HRESULT CEmPrimaryRenderWaveBuffer::GetCaps(LPVADRBUFFERCAPS pCaps)
{
CWeGrDest * pWeGrDest = (CWeGrDest *)m_pEmDevice->m_pMixDest;
HRESULT hr;
DPF_ENTER();
hr = CRenderWaveBuffer::GetCaps(pCaps);
if(SUCCEEDED(hr))
{
pCaps->dwBufferBytes = pWeGrDest->m_cbBuffer;
}
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* RequestWriteAccess
*
* Description:
* Requests write access to the primary buffer.
*
* Arguments:
* BOOL [in]: TRUE to request primary access, FALSE to relenquish it.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmPrimaryRenderWaveBuffer::RequestWriteAccess"
HRESULT CEmPrimaryRenderWaveBuffer::RequestWriteAccess(BOOL)
{
DPF_ENTER();
// WRITEPRIMARY isn't good in the WeGrDest
RPF(DPFLVL_ERROR, "The emulated device does not support WRITEPRIMARY");
DPF_LEAVE_HRESULT(DSERR_UNSUPPORTED);
return DSERR_UNSUPPORTED;
}
/***************************************************************************
*
* CommitToDevice
*
* Description:
* Commits changed buffer wave data to the device.
*
* Arguments:
* DWORD [in]: byte index into the system memory buffer of the changed
* data.
* DWORD [in]: size, in bytes, of the changed data.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmPrimaryRenderWaveBuffer::CommitToDevice"
HRESULT CEmPrimaryRenderWaveBuffer::CommitToDevice(DWORD ibCommit, DWORD cbCommit)
{
ASSERT(FALSE);
return DSERR_UNSUPPORTED;
}
/***************************************************************************
*
* GetState
*
* Description:
* Gets buffer state.
*
* Arguments:
* LPDWORD [out]: receives buffer state.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmPrimaryRenderWaveBuffer::GetState"
HRESULT CEmPrimaryRenderWaveBuffer::GetState(LPDWORD pdwState)
{
DPF_ENTER();
*pdwState = m_pEmDevice->m_dwMixerState;
DPF_LEAVE_HRESULT(DS_OK);
return DS_OK;
}
/***************************************************************************
*
* SetState
*
* Description:
* Sets buffer state.
*
* Arguments:
* DWORD [in]: buffer state.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmPrimaryRenderWaveBuffer::SetState"
HRESULT CEmPrimaryRenderWaveBuffer::SetState(DWORD dwState)
{
HRESULT hr;
DPF_ENTER();
hr = m_pEmDevice->SetMixerState(dwState);
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* GetCursorPosition
*
* Description:
* Gets the current play/write positions for the given buffer.
*
* Arguments:
* LPDWORD [out]: receives play cursor position.
* LPDWORD [out]: receives write cursor position.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmPrimaryRenderWaveBuffer::GetCursorPosition"
HRESULT CEmPrimaryRenderWaveBuffer::GetCursorPosition(LPDWORD pdwPlay, LPDWORD pdwWrite)
{
HRESULT hr;
DPF_ENTER();
ENTER_MIXER_MUTEX();
hr = m_pEmDevice->m_pMixDest->GetSamplePosition((int *)pdwPlay, (int *)pdwWrite);
LEAVE_MIXER_MUTEX();
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* Create3dListener
*
* Description:
* Creates the 3D listener.
*
* Arguments:
* C3dListener ** [out]: receives pointer to the 3D listener object.
* The caller is responsible for freeing this
* object.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmPrimaryRenderWaveBuffer::Create3dListener"
HRESULT CEmPrimaryRenderWaveBuffer::Create3dListener(C3dListener **pp3dListener)
{
C3dListener * p3dListener;
HRESULT hr;
DPF_ENTER();
ASSERT(m_vrbd.dwFlags & DSBCAPS_CTRL3D);
p3dListener = NEW(C3dListener);
hr = HRFROMP(p3dListener);
if(SUCCEEDED(hr))
{
*pp3dListener = p3dListener;
}
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* CEmSecondaryRenderWaveBuffer
*
* Description:
* Object constructor.
*
* Arguments:
* CMxRenderDevice * [in]: parent device.
* LPVOID [in]: instance identifier.
*
* Returns:
* (void)
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmSecondaryRenderWaveBuffer::CEmSecondaryRenderWaveBuffer"
CEmSecondaryRenderWaveBuffer::CEmSecondaryRenderWaveBuffer(CMxRenderDevice *pDevice, LPVOID pvInstance)
: CSecondaryRenderWaveBuffer(pDevice, pvInstance)
{
DPF_ENTER();
DPF_CONSTRUCT(CEmSecondaryRenderWaveBuffer);
// Initialize defaults
m_pMxDevice = pDevice;
m_pMixSource = NULL;
m_pFirContextLeft = NULL;
m_pFirContextRight = NULL;
m_dwState = VAD_BUFFERSTATE_STOPPED;
DPF_LEAVE_VOID();
}
/***************************************************************************
*
* ~CEmSecondaryRenderWaveBuffer
*
* Description:
* Object destructor
*
* Arguments:
* (void)
*
* Returns:
* (void)
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmSecondaryRenderWaveBuffer::~CEmSecondaryRenderWaveBuffer"
CEmSecondaryRenderWaveBuffer::~CEmSecondaryRenderWaveBuffer(void)
{
DPF_ENTER();
DPF_DESTRUCT(CEmSecondaryRenderWaveBuffer);
// Free the mixer source
DELETE(m_pMixSource);
// Free memory
MEMFREE(m_pFirContextLeft);
MEMFREE(m_pFirContextRight);
DPF_LEAVE_VOID();
}
/***************************************************************************
*
* Initialize
*
* Description:
* Initializes the wave buffer object. If this function fails, the
* object should be immediately deleted.
*
* Arguments:
* DWORD [in]: buffer flags.
* DWORD [in]: buffer size, in bytes.
* LPWAVEFORMATEX [in]: buffer format.
* CSecondaryRenderWaveBuffer * [in]: pointer to the buffer to
* duplicate from, or NULL to
* initialize as a new buffer.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmSecondaryRenderWaveBuffer::Initialize"
HRESULT CEmSecondaryRenderWaveBuffer::Initialize(LPCVADRBUFFERDESC pDesc, CEmSecondaryRenderWaveBuffer *pSource, CSysMemBuffer *pSysMemBuffer)
{
HRESULT hr = DS_OK;
DPF_ENTER();
ASSERT(LXOR(pDesc, pSource));
// Validate the buffer description
if(pDesc)
{
ASSERT(!(pDesc->dwFlags & DSBCAPS_PRIMARYBUFFER));
if(pDesc->dwFlags & DSBCAPS_LOCHARDWARE)
{
RPF(DPFLVL_ERROR, "LOCHARDWARE specified for a software buffer");
hr = DSERR_INVALIDCALL;
}
if(SUCCEEDED(hr) && !IsValidPcmWfx(pDesc->pwfxFormat))
{
hr = DSERR_BADFORMAT;
}
}
// Initialize the base class
if(SUCCEEDED(hr))
{
hr = CSecondaryRenderWaveBuffer::Initialize(pDesc, pSource, pSysMemBuffer);
}
// Set the software bit
if(SUCCEEDED(hr))
{
m_vrbd.dwFlags |= DSBCAPS_LOCSOFTWARE;
}
// Fill in the default 3D algorithm
if(SUCCEEDED(hr) && (m_vrbd.dwFlags & DSBCAPS_CTRL3D) && IS_NULL_GUID(&m_vrbd.guid3dAlgorithm))
{
m_vrbd.guid3dAlgorithm = *m_pMxDevice->GetDefault3dAlgorithm();
DPF(DPFLVL_MOREINFO, "Using default 3D algorithm " DPF_GUID_STRING, DPF_GUID_VAL(m_vrbd.guid3dAlgorithm));
}
// Allocate FIR context for the mixer
if(SUCCEEDED(hr))
{
m_pFirContextLeft = MEMALLOC(FIRCONTEXT);
hr = HRFROMP(m_pFirContextLeft);
}
if(SUCCEEDED(hr))
{
m_pFirContextRight = MEMALLOC(FIRCONTEXT);
hr = HRFROMP(m_pFirContextRight);
}
// Create the mixer source
if(SUCCEEDED(hr))
{
m_pMixSource = NEW(CMixSource(m_pMxDevice->m_pMixer));
hr = HRFROMP(m_pMixSource);
}
if(SUCCEEDED(hr))
{
hr = m_pMixSource->Initialize(m_pSysMemBuffer->GetPlayBuffer(), m_pSysMemBuffer->GetSize(), m_vrbd.pwfxFormat, &m_pFirContextLeft, &m_pFirContextRight);
}
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* Duplicate
*
* Description:
* Duplicates the buffer.
*
* Arguments:
* CSecondaryRenderWaveBuffer ** [out]: receives duplicate buffer. Use
* Release to free this object.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmSecondaryRenderWaveBuffer::Duplicate"
HRESULT CEmSecondaryRenderWaveBuffer::Duplicate(CSecondaryRenderWaveBuffer **ppBuffer)
{
CEmSecondaryRenderWaveBuffer * pBuffer = NULL;
HRESULT hr = DS_OK;
DPF_ENTER();
pBuffer = NEW(CEmSecondaryRenderWaveBuffer(m_pMxDevice, m_pvInstance));
hr = HRFROMP(pBuffer);
if(SUCCEEDED(hr))
{
hr = pBuffer->Initialize(NULL, this, NULL);
}
if(SUCCEEDED(hr))
{
*ppBuffer = pBuffer;
}
else
{
ABSOLUTE_RELEASE(pBuffer);
}
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* CommitToDevice
*
* Description:
* Commits changed buffer wave data to the device.
*
* Arguments:
* DWORD [in]: byte index into the system memory buffer of the changed
* data.
* DWORD [in]: size, in bytes, of the changed data.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmSecondaryRenderWaveBuffer::CommitToDevice"
HRESULT CEmSecondaryRenderWaveBuffer::CommitToDevice(DWORD ibCommit, DWORD cbCommit)
{
DWORD ib[2];
DWORD cb[2];
DPF_ENTER();
ENTER_MIXER_MUTEX();
// Signal a remix of this buffer
ib[0] = ibCommit;
if(ibCommit + cbCommit > m_pSysMemBuffer->GetSize())
{
cb[0] = m_vrbd.dwBufferBytes - ibCommit;
}
else
{
cb[0] = cbCommit;
}
ib[1] = 0;
cb[1] = cbCommit - cb[0];
m_pMixSource->Update(ib[0], cb[0], ib[1], cb[1]);
LEAVE_MIXER_MUTEX();
DPF_LEAVE_HRESULT(DS_OK);
return DS_OK;
}
/***************************************************************************
*
* GetState
*
* Description:
* Gets buffer state.
*
* Arguments:
* LPDWORD [out]: receives buffer state.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmSecondaryRenderWaveBuffer::GetState"
HRESULT CEmSecondaryRenderWaveBuffer::GetState(LPDWORD pdwState)
{
DPF_ENTER();
ENTER_MIXER_MUTEX();
if(m_dwState & VAD_BUFFERSTATE_STARTED)
{
if(!m_pMixSource->IsPlaying())
{
m_dwState = VAD_BUFFERSTATE_STOPPED;
}
}
*pdwState = m_dwState;
LEAVE_MIXER_MUTEX();
DPF_LEAVE_HRESULT(DS_OK);
return DS_OK;
}
/***************************************************************************
*
* SetState
*
* Description:
* Sets buffer state.
*
* Arguments:
* DWORD [in]: buffer state.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmSecondaryRenderWaveBuffer::SetState"
HRESULT CEmSecondaryRenderWaveBuffer::SetState(DWORD dwState)
{
static const DWORD dwValidMask = VAD_BUFFERSTATE_STARTED | VAD_BUFFERSTATE_LOOPING | VAD_BUFFERSTATE_SUSPEND;
DPF_ENTER();
ASSERT(IS_VALID_FLAGS(dwState, dwValidMask));
ENTER_MIXER_MUTEX();
if(dwState & VAD_BUFFERSTATE_SUSPEND)
{
ASSERT((dwState & VAD_BUFFERSTATE_SUSPEND) == VAD_BUFFERSTATE_SUSPEND);
dwState = m_dwState ^ VAD_BUFFERSTATE_SUSPEND;
}
if(dwState & VAD_BUFFERSTATE_STARTED && !(dwState & VAD_BUFFERSTATE_SUSPEND))
{
m_pMixSource->Play(MAKEBOOL(dwState & VAD_BUFFERSTATE_LOOPING));
}
else
{
m_pMixSource->Stop();
if(!(dwState & VAD_BUFFERSTATE_SUSPEND) && m_pMixSource->HasNotifications())
{
m_pMixSource->NotifyStop();
}
}
m_dwState = dwState;
LEAVE_MIXER_MUTEX();
DPF_LEAVE_HRESULT(DS_OK);
return DS_OK;
}
/***************************************************************************
*
* GetCursorPosition
*
* Description:
* Retrieves the current play and write cursor positions.
*
* Arguments:
* LPDWORD [out]: receives the play position.
* LPDWORD [out]: receives the write position.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmSecondaryRenderWaveBuffer::GetCursorPosition"
HRESULT CEmSecondaryRenderWaveBuffer::GetCursorPosition(LPDWORD pdwPlay, LPDWORD pdwWrite)
{
DPF_ENTER();
ENTER_MIXER_MUTEX();
if(m_vrbd.dwFlags & (DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_MIXIN | DSBCAPS_SINKIN | DSBCAPS_CTRLFX))
{
m_pMixSource->GetBytePosition((int *)pdwPlay, (int *)pdwWrite, NULL);
}
else
{
m_pMixSource->GetBytePosition1((int *)pdwPlay, (int *)pdwWrite);
}
LEAVE_MIXER_MUTEX();
DPF_LEAVE_HRESULT(DS_OK);
return DS_OK;
}
/***************************************************************************
*
* SetCursorPosition
*
* Description:
* Sets the current play cursor position.
*
* Arguments:
* DWORD [in]: play position.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmSecondaryRenderWaveBuffer::SetCursorPosition"
HRESULT CEmSecondaryRenderWaveBuffer::SetCursorPosition(DWORD dwPlay)
{
DPF_ENTER();
ENTER_MIXER_MUTEX();
ASSERT(dwPlay < m_vrbd.dwBufferBytes);
m_pMixSource->SetBytePosition(dwPlay);
LEAVE_MIXER_MUTEX();
DPF_LEAVE_HRESULT(DS_OK);
return DS_OK;
}
/***************************************************************************
*
* SetFrequency
*
* Description:
* Sets the buffer frequency.
*
* Arguments:
* DWORD [in]: new frequency.
* BOOL [in]: whether to clamp to the driver's supported frequency
* range if the call fails. Ignored in this class.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmSecondaryRenderWaveBuffer::SetFrequency"
HRESULT CEmSecondaryRenderWaveBuffer::SetFrequency(DWORD dwFrequency, BOOL)
{
DPF_ENTER();
ENTER_MIXER_MUTEX();
m_pMixSource->SetFrequency(dwFrequency);
m_vrbd.pwfxFormat->nSamplesPerSec = dwFrequency;
LEAVE_MIXER_MUTEX();
DPF_LEAVE_HRESULT(DS_OK);
return DS_OK;
}
/***************************************************************************
*
* SetMute
*
* Description:
* Mutes or unmutes the buffer.
*
* Arguments:
* BOOL [in]: TRUE to mute the buffer, FALSE to restore it.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmSecondaryRenderWaveBuffer::SetMute"
HRESULT CEmSecondaryRenderWaveBuffer::SetMute(BOOL fMute)
{
DPF_ENTER();
ENTER_MIXER_MUTEX();
m_pMixSource->m_fMute = fMute;
LEAVE_MIXER_MUTEX();
DPF_LEAVE_HRESULT(DS_OK);
return DS_OK;
}
/***************************************************************************
*
* SetAttenuation
*
* Description:
* Sets the attenuation for each channel.
*
* Arguments:
* PDSVOLUMEPAN [in]: attenuation.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmSecondaryRenderWaveBuffer::SetAttenuation"
HRESULT CEmSecondaryRenderWaveBuffer::SetAttenuation(PDSVOLUMEPAN pdsvp)
{
DPF_ENTER();
ENTER_MIXER_MUTEX();
m_pMixSource->SetVolumePan(pdsvp);
LEAVE_MIXER_MUTEX();
DPF_LEAVE_HRESULT(DS_OK);
return DS_OK;
}
#ifdef FUTURE_MULTIPAN_SUPPORT
/***************************************************************************
*
* SetChannelAttenuations
*
* Description:
* Sets the multichannel attenuation for a given buffer.
*
* Arguments:
* TBD.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmSecondaryRenderWaveBuffer::SetChannelAttenuations"
HRESULT CEmSecondaryRenderWaveBuffer::SetChannelAttenuations(LONG lVolume, DWORD dwChannelCount, const DWORD* pdwChannels, const LONG* plChannelVolumes)
{
HRESULT hr = DS_OK;
LONG lPan;
DPF_ENTER();
if (dwChannelCount == 0)
{
// SetChannelVolume() has not been called yet; use center panning
ASSERT(!pdwChannels && !plChannelVolumes); // Sanity checking
lPan = 0;
}
else
{
// Calculate a global LR pan value based on the channel volumes
lPan = MultiChannelToStereoPan(dwChannelCount, pdwChannels, plChannelVolumes);
}
DSVOLUMEPAN dsvp;
FillDsVolumePan(lVolume, lPan, &dsvp);
m_pMixSource->SetVolumePan(&dsvp);
DPF_LEAVE_HRESULT(hr);
return hr;
}
#endif // FUTURE_MULTIPAN_SUPPORT
/***************************************************************************
*
* SetNotificationPositions
*
* Description:
* Sets buffer notification positions.
*
* Arguments:
* DWORD [in]: DSBPOSITIONNOTIFY structure count.
* LPDSBPOSITIONNOTIFY [in]: offsets and events.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmSecondaryRenderWaveBuffer::SetNotificationPositions"
HRESULT CEmSecondaryRenderWaveBuffer::SetNotificationPositions(DWORD dwCount, LPCDSBPOSITIONNOTIFY paNotes)
{
DPF_ENTER();
ENTER_MIXER_MUTEX();
m_pMixSource->SetNotificationPositions(dwCount, paNotes);
LEAVE_MIXER_MUTEX();
DPF_LEAVE_HRESULT(DS_OK);
return DS_OK;
}
/***************************************************************************
*
* Create3dObject
*
* Description:
* Creates the 3D object.
*
* Arguments:
* REFGUID [in]: 3D algorithm GUID.
* C3dListener * [in]: listener object.
* C3dObject ** [out]: receives pointer to 3D object. The caller is
* responsible for freeing this object.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmSecondaryRenderWaveBuffer::Create3dObject"
HRESULT CEmSecondaryRenderWaveBuffer::Create3dObject(C3dListener *p3dListener, C3dObject **pp3dObject)
{
const BOOL fMute3dAtMaxDistance = MAKEBOOL(m_vrbd.dwFlags & DSBCAPS_MUTE3DATMAXDISTANCE);
HRESULT hr;
DPF_ENTER();
ASSERT(m_vrbd.dwFlags & DSBCAPS_CTRL3D);
ASSERT(IsValid3dAlgorithm(m_vrbd.guid3dAlgorithm));
m_hrSuccessCode = DS_OK;
if(DS3DALG_ITD == m_vrbd.guid3dAlgorithm)
{
hr = CreateItd3dObject(p3dListener, pp3dObject);
}
else
{
// No matter whether the 3D algorithm requested is No Virtualization (Pan3D)
// or one of the unsupported HRTF algorithms, we just do Pan3D. If HRTF had
// been requested, we return DS_NO_VIRTUALIZATION (as per manbug 23196).
if (DS3DALG_NO_VIRTUALIZATION != m_vrbd.guid3dAlgorithm)
{
m_hrSuccessCode = DS_NO_VIRTUALIZATION;
DPF(DPFLVL_INFO, "Replaced unsupported 3D algorithm " DPF_GUID_STRING " with Pan3D", DPF_GUID_VAL(m_vrbd.guid3dAlgorithm));
}
hr = CreatePan3dObject(p3dListener, fMute3dAtMaxDistance, m_vrbd.pwfxFormat->nSamplesPerSec, pp3dObject);
}
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* CreateItd3dObject
*
* Description:
* Creates the 3D object.
*
* Arguments:
* C3dListener * [in]: listener object.
* C3dObject ** [out]: receives pointer to 3D object. The caller is
* responsible for freeing this object.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmSecondaryRenderWaveBuffer::CreateItd3dObject"
HRESULT CEmSecondaryRenderWaveBuffer::CreateItd3dObject(C3dListener *p3dListener, C3dObject **pp3dObject)
{
const BOOL fMute3dAtMaxDistance = MAKEBOOL(m_vrbd.dwFlags & DSBCAPS_MUTE3DATMAXDISTANCE);
const BOOL fDopplerEnabled = !MAKEBOOL((m_vrbd.dwFlags & DSBCAPS_CTRLFX) && !(m_vrbd.dwFlags & DSBCAPS_SINKIN));
CEmItd3dObject * p3dObject;
HRESULT hr;
DPF_ENTER();
p3dObject = NEW(CEmItd3dObject(p3dListener, fMute3dAtMaxDistance, fDopplerEnabled, m_vrbd.pwfxFormat->nSamplesPerSec, m_pMixSource, m_pMxDevice->m_pMixDest, m_pFirContextLeft, m_pFirContextRight));
hr = HRFROMP(p3dObject);
if(SUCCEEDED(hr))
{
hr = p3dObject->Initialize();
}
if(SUCCEEDED(hr))
{
*pp3dObject = p3dObject;
}
else
{
RELEASE(p3dObject);
}
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* CEmItd3dObject
*
* Description:
* Object constructor.
*
* Arguments:
* C3dListener * [in]: pointer to the owning listener.
* DWORD [in]: buffer frequency.
* CMixSource * [in]: mixer source used by the owning buffer.
* PFIRCONTEXT [in]: left channel FIR context.
* PFIRCONTEXT [in]: right channel FIR context.
* BOOL [in]: TRUE to mute at max distance.
*
* Returns:
* (void)
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmItd3dObject::CEmItd3dObject"
CEmItd3dObject::CEmItd3dObject(C3dListener *pListener, BOOL fMuteAtMaxDistance, BOOL fDopplerEnabled,
DWORD dwFrequency, CMixSource *pMixSource, CMixDest *pMixDest,
PFIRCONTEXT pContextLeft, PFIRCONTEXT pContextRight)
: CItd3dObject(pListener, fMuteAtMaxDistance, fDopplerEnabled, dwFrequency)
{
DPF_ENTER();
DPF_CONSTRUCT(CEmItd3dObject);
// Initialize defaults
m_pMixSource = pMixSource;
m_pMixDest = pMixDest;
m_pContextLeft = pContextLeft;
m_pContextRight = pContextRight;
DPF_LEAVE_VOID();
}
/***************************************************************************
*
* ~CEmItd3dObject
*
* Description:
* Object destructor.
*
* Arguments:
* (void)
*
* Returns:
* (void)
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmItd3dObject::~CEmItd3dObject"
CEmItd3dObject::~CEmItd3dObject(void)
{
DPF_ENTER();
DPF_DESTRUCT(CItd3dObject);
DPF_LEAVE_VOID();
}
/***************************************************************************
*
* Commit3dChanges
*
* Description:
* Writes updated 3D data to the device.
*
* Arguments:
* (void)
*
* Returns:
* (void)
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmItd3dObject::Commit3dChanges"
HRESULT CEmItd3dObject::Commit3dChanges(void)
{
DPF_ENTER();
ENTER_MIXER_MUTEX();
// Apply changed FIR data
m_pContextLeft->fLeft = TRUE;
m_pContextRight->fLeft = FALSE;
CvtContext(&m_ofcLeft, m_pContextLeft);
CvtContext(&m_ofcRight, m_pContextRight);
// Turn the filter on or off and set proper frequency
if(DS3DMODE_DISABLE == m_opCurrent.dwMode)
{
m_pMixSource->FilterOff();
if (m_fDopplerEnabled)
m_pMixSource->SetFrequency(m_dwUserFrequency);
}
else
{
m_pMixSource->FilterOn();
if (m_fDopplerEnabled)
m_pMixSource->SetFrequency(m_dwDopplerFrequency);
}
// If 3D is enabled, and the user wants to mute at max distance AND
// we're at max distance, mute. Otherwise, unmute.
m_pMixSource->m_fMute3d = IsAtMaxDistance();
// Signal a remix
m_pMixSource->SignalRemix();
LEAVE_MIXER_MUTEX();
DPF_LEAVE_HRESULT(DS_OK);
return DS_OK;
}
/***************************************************************************
*
* CvtContext
*
* Description:
* Converts an OBJECT_ITD_CONTEXT to a FIRCONTEXT.
*
* Arguments:
* LPOBJECTFIRCONTEXT [in]: source.
* PFIRCONTEXT [out]: destination.
*
* Returns:
* (void)
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmItd3dObject::CvtContext"
void CEmItd3dObject::CvtContext(LPOBJECT_ITD_CONTEXT pSource, PFIRCONTEXT pDest)
{
DPF_ENTER();
pDest->DistAttenuation = pSource->flDistanceAttenuation;
pDest->ConeAttenuation = pSource->flConeAttenuation;
pDest->ConeShadow = pSource->flConeShadow;
pDest->PositionAttenuation = pSource->flPositionAttenuation;
pDest->PositionShadow = pSource->flPositionShadow;
pDest->VolSmoothScale = pSource->flVolSmoothScale;
pDest->VolSmoothScaleRecip = pSource->flVolSmoothScaleRecip;
pDest->VolSmoothScaleDry = pSource->flVolSmoothScaleDry;
pDest->VolSmoothScaleWet = pSource->flVolSmoothScaleWet;
pDest->iSmoothFreq = pSource->dwSmoothFreq;
pDest->iDelay = pSource->dwDelay;
pDest->TotalDryAttenuation = pSource->flPositionAttenuation * pSource->flConeAttenuation * pSource->flConeShadow * pSource->flPositionShadow;
pDest->LastDryAttenuation = pDest->TotalDryAttenuation;
pDest->TotalWetAttenuation = pSource->flPositionAttenuation * pSource->flConeAttenuation * (1.0f - pSource->flConeShadow * pSource->flPositionShadow);
pDest->LastWetAttenuation = pDest->TotalWetAttenuation;
#ifdef SMOOTH_ITD
pDest->iLastDelay = pDest->iDelay;
#endif
DPF_LEAVE_VOID();
}
/***************************************************************************
*
* Get3dOutputSampleRate
*
* Description:
* Gets the sample rate of the final output.
*
* Arguments:
* (void)
*
* Returns:
* (void)
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmItd3dObject::Get3dOutputSampleRate"
DWORD CEmItd3dObject::Get3dOutputSampleRate(void)
{
DWORD freq;
DPF_ENTER();
freq = m_pMixDest->GetFrequency();
DPF_LEAVE(freq);
return freq;
}
/***************************************************************************
*
* CEmCaptureDevice
*
* Description:
* Object constructor.
*
* Arguments:
* (void)
*
* Returns:
* (void)
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmCaptureDevice::CEmCaptureDevice"
CEmCaptureDevice::CEmCaptureDevice()
: CCaptureDevice(VAD_DEVICETYPE_EMULATEDCAPTURE)
{
DPF_ENTER();
DPF_CONSTRUCT(CEmCaptureDevice);
// Initialize defaults
m_pwfxFormat = NULL;
m_hwi = NULL;
m_fAllocated = FALSE;
DPF_LEAVE_VOID();
}
/***************************************************************************
*
* ~CEmCaptureDevice
*
* Description:
* Object destructor
*
* Arguments:
* (void)
*
* Returns:
* (void)
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmCaptureDevice::~CEmCaptureDevice"
CEmCaptureDevice::~CEmCaptureDevice()
{
DPF_ENTER();
DPF_DESTRUCT(CEmCaptureDevice);
if (m_hwi)
CloseWaveIn(&m_hwi);
// Free memory
MEMFREE(m_pwfxFormat);
DPF_LEAVE_VOID();
}
/***************************************************************************
*
* EnumDrivers
*
* Description:
* Creates a list of driver GUIDs that can be used to initialize this
* device.
*
* Arguments:
* CList& [in/out]: pointer to a CList object that will be filled with
* CDeviceDescription objects. The caller is
* responsible for freeing these objects.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmCaptureDevice::EnumDrivers"
HRESULT CEmCaptureDevice::EnumDrivers(CObjectList<CDeviceDescription> *plstDrivers)
{
CDeviceDescription * pDesc = NULL;
LPTSTR pszInterface = NULL;
HRESULT hr = DS_OK;
TCHAR szTemplate[0x100];
TCHAR szEmulated[0x100];
TCHAR szName[0x400];
UINT cDevices;
BYTE bDeviceId;
WAVEINCAPS wic;
GUID guid;
MMRESULT mmr;
DPF_ENTER();
// LIMITATION: We can't support more than 0xFF emulated devices,
// because we pack the device ID into a byte member of a GUID.
cDevices = waveInGetNumDevs();
cDevices = NUMERIC_CAST(cDevices, BYTE);
// Load string templates
if(!LoadString(hModule, IDS_DSC_DRIVERLD, szTemplate, NUMELMS(szTemplate)))
{
DPF(DPFLVL_ERROR, "Can't load driver template string");
hr = DSERR_OUTOFMEMORY;
}
if(SUCCEEDED(hr) && !LoadString(hModule, IDS_EMULATED, szEmulated, NUMELMS(szEmulated)))
{
DPF(DPFLVL_ERROR, "Can't load emulated template string");
hr = DSERR_OUTOFMEMORY;
}
// Enumerate each waveOut device and add it to the list
for(bDeviceId = 0; bDeviceId < cDevices && SUCCEEDED(hr); bDeviceId++)
{
// Get the driver GUID
g_pVadMgr->GetDriverGuid(m_vdtDeviceType, bDeviceId, &guid);
// Create the device description object
pDesc = NEW(CDeviceDescription(m_vdtDeviceType, guid, bDeviceId));
hr = HRFROMP(pDesc);
// Get the device name
if(SUCCEEDED(hr))
{
mmr = waveInGetDevCaps(bDeviceId, &wic, sizeof(wic));
hr = MMRESULTtoHRESULT(mmr);
}
if(SUCCEEDED(hr))
{
lstrcpy(szName, wic.szPname);
lstrcat(szName, szEmulated);
}
if(SUCCEEDED(hr))
{
pDesc->m_strName = szName;
}
// Get the device path
if(SUCCEEDED(hr))
{
wsprintf(szName, szTemplate, bDeviceId);
pDesc->m_strPath = szName;
}
// Get the device interface
if(SUCCEEDED(hr))
{
GetWaveDeviceInterface(bDeviceId, FALSE, &pszInterface);
pDesc->m_strInterface = pszInterface;
}
// Get the device devnode
if(SUCCEEDED(hr))
{
GetWaveDeviceDevnode(bDeviceId, FALSE, &pDesc->m_dwDevnode);
}
// Add the driver to the list
if(SUCCEEDED(hr))
{
hr = HRFROMP(plstDrivers->AddNodeToList(pDesc));
}
// Clean up
MEMFREE(pszInterface);
RELEASE(pDesc);
}
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* Initialize
*
* Description:
* Initializes the device. If this function fails, the object should
* be immediately deleted.
*
* Arguments:
* CDeviceDescription * [in]: driver description.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmCaptureDevice::Initialize"
HRESULT CEmCaptureDevice::Initialize(CDeviceDescription *pDesc)
{
HRESULT hr;
DPF_ENTER();
// Initialize the base class
hr = CCaptureDevice::Initialize(pDesc);
// Get the default format
if(SUCCEEDED(hr))
{
m_pwfxFormat = AllocDefWfx();
hr = HRFROMP(m_pwfxFormat);
}
if(SUCCEEDED(hr))
{
if(!EnumStandardFormats(m_pwfxFormat, m_pwfxFormat))
{
// If none of the formats worked, assume the device is allocated
hr = DSERR_ALLOCATED;
}
}
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* GetCaps
*
* Description:
* Fills a DSCCAPS structure with the capabilities of the device.
*
* Arguments:
* LPDSCCAPS [out]: receives caps.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmCaptureDevice::GetCaps"
HRESULT CEmCaptureDevice::GetCaps(LPDSCCAPS pCaps)
{
WAVEINCAPS wic;
MMRESULT mmr;
HRESULT hr;
DPF_ENTER();
// Query the waveIn device
mmr = waveInGetDevCaps(m_pDeviceDescription->m_uWaveDeviceId, &wic, sizeof(wic));
hr = MMRESULTtoHRESULT(mmr);
if(SUCCEEDED(hr))
{
pCaps->dwFlags = DSCCAPS_EMULDRIVER;
pCaps->dwFormats = wic.dwFormats;
pCaps->dwChannels = wic.wChannels;
}
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* CreateBuffer
*
* Description:
* Creates a capture wave buffer.
*
* Arguments:
* DWORD [in]: buffer flags.
* DWORD [in]: buffer size, in bytes.
* LPCWAVEFORMATEX [in]: buffer format.
* CCaptureWaveBuffer ** [out]: receives pointer to new wave buffer.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmCaptureDevice::CreateBuffer"
HRESULT CEmCaptureDevice::CreateBuffer
(
DWORD dwFlags,
DWORD dwBufferBytes,
LPCWAVEFORMATEX pwfxFormat,
CCaptureEffectChain*,
LPVOID pvInstance,
CCaptureWaveBuffer** ppBuffer
)
{
CEmCaptureWaveBuffer * pBuffer = NULL;
HRESULT hr = DS_OK;
DPF_ENTER();
if (m_lstBuffers.GetListHead())
{
hr = DSERR_ALLOCATED;
}
if (SUCCEEDED(hr))
{
#pragma warning(disable:4530) // Disable the nag about compiling with -GX
try
{
pBuffer = NEW(CEmCaptureWaveBuffer(this));
}
catch (...)
{
// This exception handler is silly, since it makes us leak the memory
// allocated for CEmCaptureWaveBuffer above (which wasn't assigned to
// pBuffer yet), and also possibly m_cs, which is something we really
// don't want to do if we're low on memory in the first place.
//
// But InitializeCriticalSection is supposed to be fixed in Blackcomb
// not to throw exceptions any more, so we can live with this for now.
ASSERT(pBuffer == NULL);
ASSERT(!"InitializeCriticalSection() threw an exception");
}
hr = HRFROMP(pBuffer);
}
if(SUCCEEDED(hr))
{
hr = pBuffer->Initialize(dwFlags, dwBufferBytes, pwfxFormat);
}
if(SUCCEEDED(hr))
{
*ppBuffer = pBuffer;
}
else
{
RELEASE(pBuffer);
}
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* SetGlobalFormat
*
* Description:
* Makes the specified WFX, the format for the capture buffer
*
* Arguments:
* LPVOID [in] : pointer to the owner of the format
* LPCWAVEFORMATEX [in] : pointer to the new WFX to use
* DWORD [in]: callback, if any.
* DWORD [in]: flags.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmCaptureDevice::SetGlobalFormat"
HRESULT CEmCaptureDevice::SetGlobalFormat(LPVOID pOwner, LPCWAVEFORMATEX pwfx, LPVOID pvCallback, DWORD dwFlags)
{
DPF_ENTER();
DWORD fdwOpen = 0;
LPHWAVEIN phw = &m_hwi;
// Should we attempt to use the WAVE_MAPPER?
if (DSCBCAPS_WAVEMAPPED & dwFlags)
{
fdwOpen |= WAVE_MAPPED;
}
// We don't allocate the device on focus aware buffers.
if (DSCBCAPS_FOCUSAWARE & dwFlags)
{
fdwOpen |= WAVE_FORMAT_QUERY;
phw = NULL;
}
else
{
fdwOpen |= (pvCallback ? CALLBACK_FUNCTION : CALLBACK_NULL);
// The reason why we had to close the device if open, is that
// EnumStandardFormatsCallback() used to allocate the device.
// It no longer does so as of DX 7.1 and thus we can ax this
// close. If the device is allocated, it is REALLY in use.
}
HRESULT hr = OpenWaveIn(phw, m_pDeviceDescription->m_uWaveDeviceId, pwfx, (DWORD_PTR)pvCallback, (DWORD_PTR)pOwner, fdwOpen);
if (FAILED(hr))
{
// Oops. Try to get the device back with the old format.
OpenWaveIn(phw, m_pDeviceDescription->m_uWaveDeviceId, m_pwfxFormat, (DWORD_PTR)pvCallback, (DWORD_PTR)pOwner, fdwOpen);
}
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* EnumStandardFormatsCallback
*
* Description:
* Callback function for EnumStandardFormats used when calling
* Initialize.
*
* Arguments:
* LPWAVEFORMATEX [in]: format.
*
* Returns:
* BOOL: TRUE to continue enumerating.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmCaptureDevice::EnumStandardFormatsCallback"
BOOL CEmCaptureDevice::EnumStandardFormatsCallback(LPCWAVEFORMATEX pwfx)
{
DPF_ENTER();
HRESULT hr = OpenWaveIn(NULL, m_pDeviceDescription->m_uWaveDeviceId, pwfx, 0, 0, WAVE_FORMAT_QUERY);
DPF_LEAVE(FAILED(hr));
return FAILED(hr);
}
/***************************************************************************
*
* CEmCaptureWaveBuffer
*
* Description:
* Constructor for CEmCaptureWaveBuffer
*
* Arguments:
* CCaptureVad [in]: parent object.
*
* Returns:
* Nothing
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmCaptureWaveBuffer::CEmCaptureWaveBuffer"
CEmCaptureWaveBuffer::CEmCaptureWaveBuffer(CCaptureDevice *pDevice) : CCaptureWaveBuffer(pDevice)
{
DPF_ENTER();
DPF_CONSTRUCT(CEmCaptureWaveBuffer);
ASSERT(0 == m_dwState);
ASSERT(0 == m_fdwSavedState);
ASSERT(0 == m_cwhdr);
ASSERT(NULL == m_rgpwhdr);
ASSERT(0 == m_cwhdrDone);
ASSERT(0 == m_iwhdrDone);
ASSERT(NULL == m_pBuffer);
ASSERT(0 == m_cbBuffer);
ASSERT(0 == m_cbRecordChunk);
ASSERT(NULL == m_pBufferMac);
ASSERT(NULL == m_pBufferNext);
ASSERT(0 == m_cLoops);
ASSERT(NULL == m_pwfx);
ASSERT(NULL == m_hwi);
ASSERT(0 == m_cpn);
ASSERT(NULL == m_rgpdsbpn);
ASSERT(0 == m_ipn);
ASSERT(0 == m_cpnAllocated);
m_fCritSectsValid = FALSE;
InitializeCriticalSection(&m_cs);
InitializeCriticalSection(&m_csPN);
m_fCritSectsValid = TRUE;
ASSERT(0 == m_dwCaptureCur);
ASSERT(0 == m_dwCaptureLast);
ASSERT(NULL == m_hThread);
ASSERT(NULL == m_rghEvent[0]);
ASSERT(NULL == m_rghEvent[chEvents-1]);
ASSERT(0 == m_cwhdrDropped);
ASSERT(NULL == m_pBufferProcessed);
#ifdef DEBUG_CAPTURE
ASSERT(0 == m_iwhdrExpected);
#endif
DPF_LEAVE_VOID();
}
/***************************************************************************
*
* ~CEmCaptureWaveBuffer
*
* Description:
* Destructor for CEmCaptureWaveBuffer
*
* Arguments:
* None
*
* Returns:
* Nothing
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmCaptureWaveBuffer::~CEmCaptureWaveBuffer"
CEmCaptureWaveBuffer::~CEmCaptureWaveBuffer()
{
DPF_ENTER();
DPF_DESTRUCT(CEmCaptureWaveBuffer);
// If critical section(s) not intialized, nothing else did either
if (!m_fCritSectsValid)
{
return;
}
// Set the Terminate Event so the capture thread will die
if (m_rghEvent[ihEventTerminate])
{
SetEvent(m_rghEvent[ihEventTerminate]);
}
// Wait for the thread to die, then clean up
if (m_hThread)
{
WaitObject(INFINITE, m_hThread);
CloseHandle(m_hThread);
m_hThread = NULL;
}
// Clean up our HEVENTs - set them to NULL in case
// the waveInOpen callback retires some buffers.
for (int ihEvent = chEvents-1; ihEvent >= 0; --ihEvent)
{
if (m_rghEvent[ihEvent])
{
HANDLE h = m_rghEvent[ihEvent];
m_rghEvent[ihEvent] = NULL;
CloseHandle(h);
}
}
if (m_hwi)
{
// Ignore errors since it's too late to do anything
waveInReset(m_hwi);
// Stop recording from input device, if we got cutoff
if (m_dwState & VAD_BUFFERSTATE_STARTED)
{
waveInStop(m_hwi);
}
// Need to unprepare all the headers
if (m_rgpwhdr)
{
int iwhdr;
LPWAVEHDR pwhdr;
for (iwhdr = m_cwhdr, pwhdr = m_rgpwhdr;
iwhdr > 0; --iwhdr, ++pwhdr)
{
if (WHDR_PREPARED & pwhdr->dwFlags)
{
waveInUnprepareHeader(m_hwi, pwhdr, sizeof(WAVEHDR));
}
}
}
// Close the input device
CloseWaveIn(&m_hwi);
// If this is not focus aware, mark device as unallocated.
if (!(m_dwFlags & DSCBCAPS_FOCUSAWARE) && m_pDevice)
{
((CEmCaptureDevice *)m_pDevice)->m_fAllocated = FALSE;
((CEmCaptureDevice *)m_pDevice)->m_hwi = NULL;
}
}
//==========================================================//
// Enter Critical section //
// //
EnterCriticalSection(&m_cs);
MEMFREE(m_pwfx);
MEMFREE(m_rgpwhdr);
MEMFREE(m_rgpdsbpn);
LeaveCriticalSection(&m_cs);
// //
// Leave Critical Section //
//==========================================================//
m_fCritSectsValid = FALSE;
DeleteCriticalSection(&m_csPN);
DeleteCriticalSection(&m_cs);
DPF_LEAVE_VOID();
}
/***************************************************************************
*
* Initialize
*
* Description:
* Initializes CEmCaptureWaveBuffer object
*
* Arguments:
* DWORD [in] : flags
* DWORD [in] : size of buffer in bytes
* LPCWAVEFORMATEX [in] : format for buffer
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmCaptureWaveBuffer::Initialize"
HRESULT CEmCaptureWaveBuffer::Initialize(DWORD dwFlags, DWORD dwBufferBytes, LPCWAVEFORMATEX pwfxFormat)
{
HRESULT hr;
DPF_ENTER();
// Validate params
if (!IS_VALID_READ_PTR(pwfxFormat, sizeof(WAVEFORMATEX)))
{
RPF(DPFLVL_ERROR, "Invalid wave format pointer");
goto InvalidParam;
}
if(!IsValidWfx(pwfxFormat))
{
RPF(DPFLVL_ERROR, "Invalid wave format");
goto InvalidParam;
}
if (pwfxFormat->nBlockAlign == 0 ||
dwBufferBytes < pwfxFormat->nBlockAlign ||
dwBufferBytes % pwfxFormat->nBlockAlign != 0)
{
RPF(DPFLVL_ERROR, "Invalid buffer size (must be a multiple of nBlockAlign)");
goto InvalidParam;
}
if (~DSCBCAPS_VALIDFLAGS & dwFlags)
{
RPF(DPFLVL_ERROR, "Invalid DSCBCAPS flags");
goto InvalidParam;
}
if (DSCBCAPS_CTRLFX & dwFlags)
{
RPF(DPFLVL_ERROR, "DSBCAPS_CTRLFX not allowed on emulated capture device");
goto InvalidParam;
}
m_dwFlags = dwFlags;
// Make a copy of the wave format
m_pwfx = CopyWfxAlloc(pwfxFormat);
if (NULL == m_pwfx)
{
DPF(DPFLVL_ERROR, "Unable to allocate WFX");
hr = DSERR_OUTOFMEMORY;
goto Error;
}
hr = CCaptureWaveBuffer::Initialize(dwBufferBytes);
if (FAILED(hr))
{
DPF(DPFLVL_ERROR, "Unable to initialize CCaptureWaveBuffer");
goto Error;
}
// Make a copy of important info
m_cbBuffer = m_pSysMemBuffer->GetSize();
m_pBufferProcessed = m_pBufferNext = m_pBuffer = m_pSysMemBuffer->GetWriteBuffer();
// Calculate the end of the buffer
m_pBufferMac = m_pBuffer + m_cbBuffer;
// Record Chunk should be 10 ms long to match the IRP sizes used by kmixer
m_cbRecordChunk = m_pwfx->nAvgBytesPerSec / 32;
// Round up to multiple of nBlockAlign (required for waveInAddBuffer recording)
m_cbRecordChunk = BLOCKALIGNPAD(m_cbRecordChunk, m_pwfx->nBlockAlign);
ASSERT(sizeof(m_rgszEvent[ihEventFocusChange]) >= 7+8+8+1);
wsprintf(m_rgszEvent[ihEventFocusChange], TEXT("DSC-EFC%08lX%08lX"), GetCurrentProcessId(), this);
ASSERT(sizeof(m_rgszEvent[ihEventWHDRDone]) >= 7+8+8+1);
wsprintf(m_rgszEvent[ihEventWHDRDone], TEXT("DSC-EWD%08lX%08lX"), GetCurrentProcessId(), this);
ASSERT(sizeof(m_rgszEvent[ihEventTerminate]) >= 6+8+8+1);
wsprintf(m_rgszEvent[ihEventTerminate], TEXT("DSC-ET%08lX%08lX"), GetCurrentProcessId(), this);
ASSERT(sizeof(m_rgszEvent[ihEventThreadStart]) >= 7+8+8+1);
wsprintf(m_rgszEvent[ihEventThreadStart], TEXT("DSC-ETS%08lX%08lX"), GetCurrentProcessId(), this);
// The first two events we want to be auto-reset
// The third event, we want to stay signaled until reset
static const BOOL rgfEvent[chEvents] = {FALSE, FALSE, FALSE, TRUE};
for (int ihEvent = 0; ihEvent < chEvents; ++ihEvent)
{
m_rghEvent[ihEvent] = CreateEvent(NULL, rgfEvent[ihEvent], FALSE, m_rgszEvent[ihEvent]);
if (NULL == m_rghEvent[ihEvent])
{
RPF(DPFLVL_ERROR, "Unable to create event");
hr = WIN32ERRORtoHRESULT(GetLastError());
goto Error;
}
}
// Attempt to set the selected format
CEmCaptureDevice *pDevice = (CEmCaptureDevice *)m_pDevice;
hr = pDevice->SetGlobalFormat(this, m_pwfx, waveInCallback, dwFlags);
m_hwi = pDevice->HWaveIn();
if (FAILED(hr))
{
RPF(DPFLVL_ERROR, "Unable to set global device format");
goto Error;
}
// Calculate number of blocks of size m_cbRecordChunk bytes
m_cwhdr = m_cbBuffer / m_cbRecordChunk;
// See if we have a partial-sized last block
if (m_cbBuffer % m_cbRecordChunk)
++m_cwhdr;
// Create at most cwhdrDefault WAVEHDRs
if (m_cwhdr > cwhdrDefault)
m_cwhdr = cwhdrDefault;
ASSERT(m_cwhdr > 0);
m_cwhdrDropped = m_cwhdr;
// Allocate space for WAVEHDR arrays
m_rgpwhdr = MEMALLOC_A(WAVEHDR, m_cwhdr);
if (NULL == m_rgpwhdr)
{
DPF(DPFLVL_ERROR, "Unable to allocate WAVEHDRs");
hr = DSERR_OUTOFMEMORY;
goto Error;
}
// Create worker thread
DWORD dwThreadID;
m_hThread = CreateThread(NULL, 0, CEmCaptureWaveBuffer::CaptureThreadStatic, this, 0, &dwThreadID);
if (NULL == m_hThread)
{
RPF(DPFLVL_ERROR, "Unable to create thread");
hr = WIN32ERRORtoHRESULT(GetLastError());
goto Error;
}
// If this is not focus aware, mark device as allocated.
if (!(m_dwFlags & DSCBCAPS_FOCUSAWARE))
{
pDevice->m_fAllocated = TRUE;
}
hr = DS_OK;
Error:
DPF_LEAVE_HRESULT(hr);
return hr;
InvalidParam:
hr = DSERR_INVALIDPARAM;
goto Error;
}
/***************************************************************************
*
* GetCaps
*
* Description:
* Gets capabilities for the device.
*
* Arguments:
* LPDSCBCAPS [out]: receives caps.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmCaptureWaveBuffer::GetCaps"
HRESULT CEmCaptureWaveBuffer::GetCaps(LPDSCBCAPS pDsbcCaps)
{
DPF_ENTER();
ASSERT(sizeof(*pDsbcCaps) == pDsbcCaps->dwSize);
pDsbcCaps->dwFlags = m_dwFlags;
pDsbcCaps->dwBufferBytes = m_pSysMemBuffer->GetSize();
pDsbcCaps->dwReserved = 0;
DPF_LEAVE_HRESULT(DS_OK);
return DS_OK;
}
/***************************************************************************
*
* GetState
*
* Description:
* Gets buffer state.
*
* Arguments:
* LPDWORD [out]: receives buffer state.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmCaptureWaveBuffer::GetState"
HRESULT CEmCaptureWaveBuffer::GetState(LPDWORD pdwState)
{
DPF_ENTER();
ASSERT(pdwState);
*pdwState = m_dwState & VAD_SETSTATE_MASK;
DPF_LEAVE_HRESULT(DS_OK);
return DS_OK;
}
/***************************************************************************
*
* GetCursorPosition
*
* Description:
* Gets the current capture/read positions for the given buffer.
*
* Arguments:
* LPDWORD [out]: receives capture cursor position.
* LPDWORD [out]: receives read cursor position.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmCaptureWaveBuffer::GetCursorPosition"
HRESULT CEmCaptureWaveBuffer::GetCursorPosition(LPDWORD pdwCapturePosition, LPDWORD pdwReadPosition)
{
HRESULT hr = DS_OK;
DWORD dwRead;
DPF_ENTER();
if (!(DSCBCAPS_FOCUSAWARE & m_dwFlags))
{
// If we've successfully opened the waveIn device
if (NULL == m_hwi)
{
hr = DSERR_INVALIDPARAM;
goto Error;
}
}
dwRead = ((m_dwCaptureCur + m_dwCaptureLast) % m_cbBuffer);
// get the current positions
if (pdwReadPosition)
{
*pdwReadPosition = dwRead;
}
if (pdwCapturePosition)
{
MMTIME mmt;
MMRESULT mmr;
//==========================================================//
// Enter Critical section //
// //
EnterCriticalSection(&m_cs);
if (m_hwi)
{
mmt.wType = TIME_BYTES;
mmr = waveInGetPosition(m_hwi, &mmt, sizeof(mmt));
if (MMSYSERR_NOERROR != mmr)
{
hr = MMRESULTtoHRESULT(mmr);
}
else
{
if ((TIME_BYTES == mmt.wType) && (m_dwState & VAD_BUFFERSTATE_STARTED))
{
*pdwCapturePosition = ((mmt.u.cb + m_dwCaptureLast) % m_cbBuffer);
}
else
{
// Don't know how to handle anything other than TIME_BYTES so
// we fall back to using the current valid recorded data offset
*pdwCapturePosition = dwRead;
}
}
}
else
{
// This is a focus aware buffer, and it is stopped.
*pdwCapturePosition = dwRead;
}
LeaveCriticalSection(&m_cs);
// //
// Leave Critical Section //
//==========================================================//
}
Error:
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* SetNotificationPositions
*
* Description:
* Sets buffer notification positions.
*
* Arguments:
* DWORD [in]: DSBPOSITIONNOTIFY structure count.
* LPDSBPOSITIONNOTIFY [in]: offsets and events.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmCaptureWaveBuffer::SetNotificationPositions"
HRESULT CEmCaptureWaveBuffer::SetNotificationPositions(DWORD cpn, LPCDSBPOSITIONNOTIFY pdsbpn)
{
HRESULT hr = DS_OK;
DPF_ENTER();
//==========================================================//
// Enter Critical section //
// //
ASSERT(m_fCritSectsValid);
EnterCriticalSection(&m_csPN);
if (cpn)
{
// need to grow array?
if (m_cpnAllocated < cpn)
{
LPDSBPOSITIONNOTIFY ppnT;
ppnT = MEMALLOC_A(DSBPOSITIONNOTIFY, cpn);
if (NULL == ppnT)
{
hr = DSERR_OUTOFMEMORY;
goto Done;
}
MEMFREE(m_rgpdsbpn);
m_rgpdsbpn = ppnT;
m_cpnAllocated = cpn;
}
CopyMemory(m_rgpdsbpn, pdsbpn, sizeof(DSBPOSITIONNOTIFY)*cpn);
}
m_cpn = cpn;
m_ipn = 0;
Done:
LeaveCriticalSection(&m_csPN);
// //
// Leave Critical Section //
//==========================================================//
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* SetState
*
* Description:
* Sets buffer state.
*
* Arguments:
* DWORD [in]: buffer state.
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmCaptureWaveBuffer::SetState"
HRESULT CEmCaptureWaveBuffer::SetState(DWORD dwState)
{
HRESULT hr = DS_OK;
MMRESULT mmr = MMSYSERR_NOERROR;
DWORD fdwOpen;
DPF_ENTER();
ASSERT(IS_VALID_FLAGS(dwState, VAD_SETSTATE_MASK));
ASSERT(m_hwi || (m_dwFlags & DSCBCAPS_FOCUSAWARE));
//==========================================================//
// Enter Critical section //
// //
ASSERT(m_fCritSectsValid);
EnterCriticalSection(&m_cs);
if (dwState != m_dwState)
{
if (dwState & VAD_BUFFERSTATE_STARTED) // Need to start capturing
{
BOOL fStarted = TRUE;
// If we're focus aware, check if we have focus...
if (DSCBCAPS_FOCUSAWARE & m_dwFlags)
{
if (m_dwState & VAD_BUFFERSTATE_INFOCUS)
{
fdwOpen = CALLBACK_FUNCTION;
fdwOpen |= ((DSCBCAPS_WAVEMAPPED & m_dwFlags) ? WAVE_MAPPED : 0);
if (NULL == m_hwi)
{
hr = OpenWaveIn(&m_hwi, m_pDevice->m_pDeviceDescription->m_uWaveDeviceId, m_pwfx,
(DWORD_PTR)CEmCaptureWaveBuffer::waveInCallback, (DWORD_PTR)this, fdwOpen);
}
}
else
{
// Set these flags so that when the buffer DOES get
// focus, it will start properly
DPF(DPFLVL_MOREINFO, "Start called but buffer has no focus: 0x%08lx (%08lx)", m_dwState, this);
m_fdwSavedState |= (dwState & (VAD_BUFFERSTATE_STARTED | VAD_BUFFERSTATE_LOOPING));
fStarted = FALSE;
}
}
if (SUCCEEDED(hr) && fStarted)
{
// Make sure worker thread is running
DWORD dwResult = WaitObject(INFINITE, m_rghEvent[ihEventThreadStart]);
ASSERT(WAIT_OBJECT_0 == dwResult);
// Are we not capturing yet?
if (!(m_dwState & VAD_BUFFERSTATE_STARTED))
{
LONG iwhdr = 0;
while ((m_cwhdrDropped > 0) &&
((dwState & VAD_BUFFERSTATE_LOOPING) || (m_cLoops == 0)))
{
LPWAVEHDR pwhdr = m_rgpwhdr + iwhdr;
hr = QueueWaveHeader(pwhdr);
if (FAILED(hr))
{
DPF(DPFLVL_ERROR, "QueueWaveHeader failed");
goto Error;
}
++iwhdr;
ASSERT(iwhdr <= m_cwhdr);
InterlockedDecrement(&m_cwhdrDropped);
}
#ifdef DEBUG
if (m_cwhdrDropped != 0)
ASSERT(!(VAD_BUFFERSTATE_LOOPING & dwState));
#endif
// Calling waveInStart more than once doesn't result in errors
mmr = waveInStart(m_hwi);
hr = MMRESULTtoHRESULT(mmr);
if (SUCCEEDED(hr))
{
// We're not stopped, we're in capture mode now
m_dwState &= ~(DSCBSTATUS_STOPPING | DSCBSTATUS_STOPPED);
m_dwState |= VAD_BUFFERSTATE_STARTED;
m_fdwSavedState |= VAD_BUFFERSTATE_STARTED;
// Are we looping?
if (VAD_BUFFERSTATE_LOOPING & dwState)
{
m_dwState |= VAD_BUFFERSTATE_LOOPING;
m_fdwSavedState |= VAD_BUFFERSTATE_LOOPING;
}
else
{
m_dwState &= ~VAD_BUFFERSTATE_LOOPING;
m_fdwSavedState &= ~VAD_BUFFERSTATE_LOOPING;
}
// Update to next WAVEHDR expected
m_iwhdrDone = 0;
// Remember last valid position
m_dwCaptureLast += m_dwCaptureCur;
m_dwCaptureCur = 0;
}
}
}
}
else if (dwState == VAD_BUFFERSTATE_INFOCUS)
{
// Focus-aware buffers start capturing when they gain focus
if ((m_dwFlags & DSCBCAPS_FOCUSAWARE) &&
!((CEmCaptureDevice*)m_pDevice)->m_fAllocated)
{
// Update m_dwState according to the dwState argument
m_dwState &= ~VAD_FOCUSFLAGS;
m_dwState |= VAD_BUFFERSTATE_INFOCUS;
// Signal CaptureThread to handle the state change
if (!(m_dwState & VAD_BUFFERSTATE_STARTED))
{
HANDLE hEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, m_rgszEvent[ihEventFocusChange]);
ASSERT(hEvent);
SetEvent(hEvent);
CloseHandle(hEvent);
}
}
}
else if (dwState & (VAD_BUFFERSTATE_OUTOFFOCUS | VAD_BUFFERSTATE_LOSTCONSOLE))
{
// If we are focus-aware and the capture focus state is changing...
if ((m_dwFlags & DSCBCAPS_FOCUSAWARE) &&
(m_dwState & VAD_FOCUSFLAGS) != dwState)
{
// Update m_dwState according to the dwState argument
m_dwState &= ~VAD_FOCUSFLAGS;
m_dwState |= dwState;
// Signal CaptureThread to handle the state change
HANDLE hEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, m_rgszEvent[ihEventFocusChange]);
ASSERT(hEvent);
SetEvent(hEvent);
CloseHandle(hEvent);
}
}
else // VAD_BUFFERSTATE_STOPPED case; need to stop capturing
{
ASSERT(dwState == VAD_BUFFERSTATE_STOPPED); // By elimination
// Are we currently capturing?
if (VAD_BUFFERSTATE_STARTED & m_dwState)
{
// We're stopping capturing data
m_dwState |= DSCBSTATUS_STOPPING;
// Stop recording from input device
if (m_hwi)
{
// Make sure buffers are flushed
mmr = waveInReset(m_hwi);
#ifdef DEBUG_CAPTURE
DPF(DPFLVL_INFO, "Called waveInReset(0x%08lx) = 0x%08lx", m_hwi, mmr);
#endif
mmr = waveInStop(m_hwi);
#ifdef DEBUG_CAPTURE
DPF(DPFLVL_INFO, "Called waveInStop(0x%08lx) = 0x%08lx", m_hwi, mmr);
#endif
}
hr = MMRESULTtoHRESULT(mmr);
// We've stopped capturing data
m_dwState |= DSCBSTATUS_STOPPED;
m_dwState &= ~(VAD_BUFFERSTATE_STARTED | VAD_BUFFERSTATE_LOOPING);
// CaptureThread will handle STOP position notifications
// when the last living WAVEHDR is processed
}
// Not looping; not capturing.
m_fdwSavedState = 0L;
}
}
Error:
LeaveCriticalSection(&m_cs);
// //
// Leave Critical Section //
//==========================================================//
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* NotifyStop
*
* Description:
* Sets any events that are supposed to set when capturing stops
*
* Arguments:
* None.
*
* Returns:
* None.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmCaptureWaveBuffer::NotifyStop"
void CEmCaptureWaveBuffer::NotifyStop(void)
{
DPF_ENTER();
//==========================================================//
// Enter Critical section //
// //
ASSERT(m_fCritSectsValid);
EnterCriticalSection(&m_csPN);
// Signal any STOP notifications - only one allowed
//
if ((m_cpn > 0) && (DSBPN_OFFSETSTOP == m_rgpdsbpn[m_cpn-1].dwOffset))
{
// SetEvent can fault if handle was cleaned up out from under us
// on process termination. In this case we will try to do a stop
// notification when the capture buffer is being destroyed.
try
{
SetEvent(m_rgpdsbpn[m_cpn-1].hEventNotify);
}
catch (...) {}
}
LeaveCriticalSection(&m_csPN);
// //
// Leave Critical Section //
//==========================================================//
DPF_LEAVE_VOID();
}
/***************************************************************************
*
* QueueWaveHeader
*
* Description:
* Queues wave header in the waveIn queue
*
* Arguments:
* LPWAVEHDR [in] : WAVEHDR to queue
*
* Returns:
* HRESULT: DirectSound/COM result code.
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmCaptureWaveBuffer::QueueWaveHeader"
HRESULT CEmCaptureWaveBuffer::QueueWaveHeader(LPWAVEHDR pwhdr)
{
DPF_ENTER();
pwhdr->lpData = (LPSTR)m_pBufferNext;
// does capture buffer extend beyond end of buffer?
if ((m_pBufferNext + m_cbRecordChunk) > m_pBufferMac)
{
// then use whatever is left
// this should only happen on last WAVEHDR
pwhdr->dwBufferLength = PtrDiffToUlong(m_pBufferMac - m_pBufferNext);
}
else
{
pwhdr->dwBufferLength = m_cbRecordChunk;
}
ASSERT((LPBYTE)pwhdr->lpData == m_pBufferNext);
ASSERT((m_pBufferNext + pwhdr->dwBufferLength) <= m_pBufferMac);
#ifdef DEBUG_CAPTURE
// OutputDbgWHDR("Queue: ", pwhdr - m_rgpwhdr, pwhdr); // Vestiges of old debug traces
#endif
ASSERT(m_hwi);
ASSERT(!(pwhdr->dwFlags & WHDR_PREPARED));
ASSERT(!(pwhdr->dwFlags & WHDR_DONE));
MMRESULT mmr = waveInPrepareHeader(m_hwi, pwhdr, sizeof(WAVEHDR));
if (MMSYSERR_NOERROR == mmr)
{
mmr = waveInAddBuffer(m_hwi, pwhdr, sizeof(WAVEHDR));
if (MMSYSERR_NOERROR != mmr)
{
DPF(DPFLVL_ERROR, "waveInAddBuffer failed: 0x%08lx", mmr);
ASSERT(MMSYSERR_NOERROR == mmr);
}
// Mark header as queued
pwhdr->dwUser = 0xdead0000;
}
else
{
DPF(DPFLVL_ERROR, "waveInPrepareHeader failed: 0x%08lx", mmr);
ASSERT(MMSYSERR_NOERROR == mmr);
}
if (MMSYSERR_NOERROR != mmr)
{
waveInReset(m_hwi);
DPF(DPFLVL_INFO, "Called waveInReset()");
}
else
{
m_pBufferNext += pwhdr->dwBufferLength;
// Wraparound?
if (m_pBufferNext >= m_pBufferMac)
{
m_pBufferNext = m_pBuffer;
m_cLoops += 1;
}
}
HRESULT hr = MMRESULTtoHRESULT(mmr);
DPF_LEAVE_HRESULT(hr);
return hr;
}
/***************************************************************************
*
* waveInCallback
*
* Description:
* Called by system when a WAVEHDR has been processed
*
* Arguments:
* HWAVEIN [in] :
* UINT [in] :
* DWORD [in] :
* DWORD [in] :
* DWORD [in] :
*
* Returns:
* Nothing
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmCaptureWaveBuffer::waveInCallback"
void CALLBACK CEmCaptureWaveBuffer::waveInCallback(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
CEmCaptureWaveBuffer *pThis = (CEmCaptureWaveBuffer *)dwInstance;
if (WIM_DATA == uMsg && pThis->m_rghEvent[ihEventWHDRDone])
{
// Need this check for pThis->m_rghEvent[ihEventWHDRDone] != 0 here, because
// during shutdown we may free up the event and still get some last callbacks
InterlockedIncrement(&pThis->m_cwhdrDone);
SetEvent(pThis->m_rghEvent[ihEventWHDRDone]);
#ifdef DEBUG_CAPTURE
DWORD iwhdr = (LPWAVEHDR)dwParam1 - pThis->m_rgpwhdr;
if (iwhdr != pThis->m_iwhdrExpected)
DPF(DPFLVL_ERROR, "Expected wave header #%u, and got #u instead!", pThis->m_iwhdrExpected, iwhdr);
pThis->m_iwhdrExpected = (iwhdr + 1) % pThis->m_cwhdr;
// OutputDbgWHDR("Callback: ", iwhdr, pwhdr); // Vestiges of old debug traces
#endif
// Mark as done from the callback function
((LPWAVEHDR)dwParam1)->dwUser = 0xdead0001;
}
}
/***************************************************************************
*
* CaptureThreadStatic
*
* Description:
* Static helper function used to launch CaptureThread.
*
* Arguments:
* LPVOID [in] : pointer to instance data
*
* Returns:
* DWORD: return code (ignored - always 0)
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmCaptureWaveBuffer::CaptureThread"
DWORD WINAPI CEmCaptureWaveBuffer::CaptureThreadStatic(LPVOID pv)
{
((CEmCaptureWaveBuffer*)pv)->CaptureThread();
return 0;
}
/***************************************************************************
*
* CaptureThread
*
* Description:
* Processes WAVEHDRs and requeues them if necessary.
*
* Arguments:
* (void)
*
* Returns:
* (void)
*
***************************************************************************/
#undef DPF_FNAME
#define DPF_FNAME "CEmCaptureWaveBuffer::CaptureThread"
void CEmCaptureWaveBuffer::CaptureThread()
{
HANDLE rghEvent[3] = { m_rghEvent[ihEventTerminate],
m_rghEvent[ihEventWHDRDone],
m_rghEvent[ihEventFocusChange] };
HRESULT hr;
ASSERT(m_rghEvent[ihEventThreadStart]);
SetEvent(m_rghEvent[ihEventThreadStart]);
// DSOUND does a similar thing for the waveOut thread
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
while (TRUE)
{
// Wait for a Terminate or Capture Begin Event
DWORD dwResultWait = WaitObjectArray(NUMELMS(rghEvent), INFINITE, FALSE, rghEvent);
// Terminate?
if (WAIT_OBJECT_0 + ihEventTerminate == dwResultWait)
break;
EnterCriticalSection(&m_cs);
// Is this a focus change?
if (WAIT_OBJECT_0 + ihEventFocusChange == dwResultWait)
{
DPF(DPFLVL_MOREINFO, "Focus change notification: 0x%08lx", m_dwState);
if (m_dwState & VAD_BUFFERSTATE_INFOCUS)
{
if (VAD_BUFFERSTATE_STARTED & m_fdwSavedState)
{
m_dwState &= ~(DSCBSTATUS_STOPPED | DSCBSTATUS_STOPPING);
m_dwState |= DSCBSTATUS_CAPTURING;
if (VAD_BUFFERSTATE_LOOPING & m_fdwSavedState)
{
m_dwState |= VAD_BUFFERSTATE_LOOPING;
}
}
else
{
// Buffer is already started, simply leave.
LeaveCriticalSection(&m_cs);
continue;
}
DPF(DPFLVL_MOREINFO, "Focus starting thread.");
hr = DS_OK;
if (m_hwi == NULL)
{
DWORD fdwOpen = CALLBACK_FUNCTION;
fdwOpen |= ((DSCBCAPS_WAVEMAPPED & m_dwFlags) ? WAVE_MAPPED : 0);
for (UINT jj = 4; jj; jj--)
{
hr = OpenWaveIn(&m_hwi, m_pDevice->m_pDeviceDescription->m_uWaveDeviceId,
m_pwfx, (DWORD_PTR)waveInCallback, (DWORD_PTR)this, fdwOpen);
if (SUCCEEDED(hr))
break;
DPF(DPFLVL_MOREINFO, "Focus change: waveInOpen failed with 0x%08lx; retrying after 20ms", hr);
Sleep(20);
}
}
if (SUCCEEDED(hr))
{
LONG ii = 0;
while ((m_cwhdrDropped > 0) &&
((m_dwState & VAD_BUFFERSTATE_LOOPING) || (m_cLoops == 0)))
{
LPWAVEHDR pwhdr = m_rgpwhdr + ii;
hr = QueueWaveHeader(pwhdr);
if (FAILED(hr))
{
DPF(DPFLVL_ERROR, "QueueWaveHeader failed");
ASSERT(FALSE);
}
++ii;
ASSERT(ii <= m_cwhdr);
InterlockedDecrement(&m_cwhdrDropped);
}
MMRESULT mmr = waveInStart(m_hwi);
ASSERT(MMSYSERR_NOERROR == mmr);
m_iwhdrDone = 0;
}
}
else
{
if (m_dwState & VAD_BUFFERSTATE_STARTED)
{
DPF(DPFLVL_MOREINFO, "Losing focus, stopping buffer");
m_dwState &= ~VAD_BUFFERSTATE_STOPPED;
m_dwState |= DSCBSTATUS_STOPPING;
}
else
{
// Buffer is already stopped, simply leave.
LeaveCriticalSection(&m_cs);
continue;
}
if (m_hwi)
{
DPF(DPFLVL_MOREINFO, "Focus stopping thread");
waveInReset(m_hwi);
}
}
if (m_hEventFocus)
{
SetEvent(m_hEventFocus);
}
LeaveCriticalSection(&m_cs);
continue;
}
#ifdef DEBUG_CAPTURE
DPF(DPFLVL_INFO, "Capture thread wakes");
#endif
// If we get here, we must have been signaled on the only other
// event we were listening for. Let's make sure of that anyway.
ASSERT(WAIT_OBJECT_0 + ihEventWHDRDone == dwResultWait);
LONG l = InterlockedDecrement(&m_cwhdrDone);
while (l >= 0)
{
// Quickly check if we should terminate
dwResultWait = WaitObject(0, m_rghEvent[ihEventTerminate]);
if (WAIT_OBJECT_0 == dwResultWait)
break;
WAVEHDR *pwhdr = m_rgpwhdr + m_iwhdrDone;
#ifdef DEBUG_CAPTURE
DPF(DPFLVL_INFO, "Processing header #%u (pwhdr=0x%08lx)", m_iwhdrDone, pwhdr);
// OutputDbgWHDR("Thread: ", m_iwhdrDone, pwhdr); // Vestiges of old debug traces
#endif
if (pwhdr->dwBytesRecorded)
{
BOOL fEndOfBuffer = FALSE;
// Update number of recorded bytes
m_dwCaptureCur += pwhdr->dwBytesRecorded;
ASSERT(m_pBufferProcessed == (LPBYTE)pwhdr->lpData);
m_pBufferProcessed += pwhdr->dwBytesRecorded;
if (m_pBufferProcessed >= m_pBufferMac)
{
m_pBufferProcessed = m_pBuffer;
fEndOfBuffer = TRUE;
}
// Grab critical section for position notify handling
//==========================================================//
// Enter Critical section //
// //
EnterCriticalSection(&m_csPN);
// Scan for any position notifies that need to be signaled
if (m_cpn)
{
DWORD ipnOld = m_ipn;
DWORD dwBufferStart = PtrDiffToUlong((LPBYTE)pwhdr->lpData - m_pBuffer);
DWORD dwBufferEnd = dwBufferStart + pwhdr->dwBytesRecorded;
// Is there a position.notify within the start.end of this
// captured data?
// Is the current position.notify to be signaled on Stop?
while (((m_rgpdsbpn[m_ipn].dwOffset >= dwBufferStart) &&
(m_rgpdsbpn[m_ipn].dwOffset < dwBufferEnd)) ||
(DSBPN_OFFSETSTOP == m_rgpdsbpn[m_ipn].dwOffset))
{
// Only signal if not for Stop pos.notify
if (DSBPN_OFFSETSTOP != m_rgpdsbpn[m_ipn].dwOffset)
{
SetEvent(m_rgpdsbpn[m_ipn].hEventNotify);
}
// go on to the next pos.notify
++m_ipn;
// wraparound?
if (m_ipn >= m_cpn)
{
m_ipn = 0;
}
// Infinite loop?
if (m_ipn == ipnOld)
{
break;
}
}
}
LeaveCriticalSection(&m_csPN);
// //
// Leave Critical Section //
//==========================================================//
// Transition buffer to stop state:
// if the capture buffer end has been reached AND
// if the buffer is non-LOOPING AND
// if the buffer isn't in the middle of stopping
//
// Do this after the position notifications since STOP notification
// is after any buffer-offset notification.
if (fEndOfBuffer &&
!(m_dwState & VAD_BUFFERSTATE_LOOPING) &&
!(m_dwState & DSCBSTATUS_STOPPING))
{
SetState(VAD_BUFFERSTATE_STOPPED);
ASSERT(m_dwState & DSCBSTATUS_STOPPING);
ASSERT((m_cwhdrDropped+1) == m_cwhdr);
}
}
// Clear the WHDR_DONE flag
pwhdr->dwFlags &= ~WHDR_DONE;
// Reset to zero
pwhdr->dwBytesRecorded = 0;
MMRESULT mmr = waveInUnprepareHeader(m_hwi, pwhdr, sizeof(WAVEHDR));
if (MMSYSERR_NOERROR != mmr)
{
DPF(DPFLVL_ERROR, "waveInUnprepareHeader returned %ld", mmr);
if (WAVERR_STILLPLAYING == mmr)
DPF(DPFLVL_ERROR, "which is WAVERR_STILLPLAYING; reactivate bug 340919");
}
ASSERT(MMSYSERR_NOERROR == mmr);
// We're stopping, let's drop everything
if (m_dwState & DSCBSTATUS_STOPPING)
{
Drop:
InterlockedIncrement(&m_cwhdrDropped);
// When all WAVEHDRs have been dropped
if (m_cwhdrDropped == m_cwhdr)
{
// Set the next point in the data buffer to capture to
m_pBufferNext = m_pBufferProcessed;
m_cLoops = 0;
// Notify user that we've stopped
NotifyStop();
// Focus aware buffers release the device on stop
if (DSCBCAPS_FOCUSAWARE & m_dwFlags)
{
if (m_hwi)
{
hr = CloseWaveIn(&m_hwi);
#ifdef DEBUG_CAPTURE
DPF(DPFLVL_INFO, "Closed waveIn and reset m_hwi = NULL");
#endif
ASSERT(SUCCEEDED(hr));
}
m_dwState &= ~(VAD_BUFFERSTATE_STARTED | VAD_BUFFERSTATE_LOOPING);
}
}
}
else
{
ASSERT(m_dwState & VAD_BUFFERSTATE_STARTED);
// If we're LOOPING or we haven't reached the end of the buffer yet
// then put the WAVEHDR back on to the queue with a new position
// in the buffer, etc.
BOOL fAddToQueue = (m_dwState & VAD_BUFFERSTATE_LOOPING) ||
(m_pBufferNext > (LPBYTE)pwhdr->lpData);
if (fAddToQueue)
{
BOOL fDrop;
if (m_dwState & (DSCBSTATUS_STOPPED | DSCBSTATUS_STOPPING))
{
fDrop = TRUE;
}
else
{
hr = QueueWaveHeader(pwhdr);
ASSERT(SUCCEEDED(hr));
fDrop = FALSE;
}
if (fDrop)
{
goto Drop;
}
}
else
{
InterlockedIncrement(&m_cwhdrDropped);
// If no WAVEHDRs are queued then if the user
// starts capturing again, we queue the WAVEHDRs from
// the beginning of our array
if (m_cwhdr == m_cwhdrDropped)
{
#ifdef DEBUG
if (!(m_dwState & VAD_BUFFERSTATE_LOOPING))
ASSERT(m_cLoops > 0);
#endif
// Notify user that we've stopped
NotifyStop();
}
}
}
++m_iwhdrDone;
if (m_iwhdrDone >= m_cwhdr)
{
m_iwhdrDone = 0;
}
// On to the next one
l = InterlockedDecrement(&m_cwhdrDone);
}
InterlockedIncrement(&m_cwhdrDone);
if (WAIT_OBJECT_0 == dwResultWait)
{
LeaveCriticalSection(&m_cs);
break;
}
LeaveCriticalSection(&m_cs);
}
}