2025-11-28 00:35:46 +09:00

1395 lines
35 KiB
C++

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright © Microsoft Corporation. All rights reserved
// SpAudioPlug.cpp : Implementation of SpAudioPlug and DLL registration.
#include "stdafx.h"
#include "SimpleAudio.h"
#include "SpAudioPlug.h"
/////////////////////////////////////////////////////////////////////////////
//
template <class T>
void CBasicQueueByArray<T>::_Clear()
{
m_ulQueueSize = 0;
m_ulHeader = 0;
m_ulTail = 0;
m_ullTotalOut = 0;
m_ullTotalIn = 0;
m_ulSpaceNotifySize = 1;
m_ulDataNotifySize = 1;
if (m_pCoMemAlloc)
{
::CoTaskMemFree(m_pCoMemAlloc);
m_pCoMemAlloc = NULL;
}
}
template <class T>
ULONG CBasicQueueByArray<T>::_SpaceSize()
{
return (m_ulHeader >= m_ulTail) ? (m_ulQueueSize - (m_ulHeader - m_ulTail) - 1) : (m_ulTail - m_ulHeader - 1);
}
template <class T>
ULONG CBasicQueueByArray<T>::_DataSize()
{
return (m_ulHeader >= m_ulTail) ? (m_ulHeader - m_ulTail) : (m_ulQueueSize - (m_ulTail - m_ulHeader));
}
template <class T>
CBasicQueueByArray<T>::CBasicQueueByArray() :
m_pCoMemAlloc(NULL),
m_ulQueueSize(0),
m_ulHeader(0),
m_ulTail(0),
m_hSpaceAvailable(0),
m_hDataAvailable(0),
m_ullTotalOut(0),
m_ullTotalIn(0),
m_ulSpaceNotifySize(1),
m_ulDataNotifySize(1)
{
InitializeCriticalSection(&m_CriticalSection);
}
template <class T>
CBasicQueueByArray<T>::CBasicQueueByArray(ULONG ulQueueSize, HRESULT *phr) :
m_pCoMemAlloc(NULL),
m_ulQueueSize(0),
m_ulHeader(0),
m_ulTail(0),
m_hSpaceAvailable(0),
m_hDataAvailable(0),
m_ullTotalOut(0),
m_ullTotalIn(0),
m_ulSpaceNotifySize(1),
m_ulDataNotifySize(1)
{
InitializeCriticalSection(&m_CriticalSection);
*phr = Init(ulQueueSize);
}
template <class T>
HRESULT CBasicQueueByArray<T>::Resize(ULONG ulNewQueueSize)
{
CRITICAL_SECTION_AUTO_LOCK csl(this);
HRESULT hr = S_OK;
if (ulNewQueueSize > m_ulQueueSize-1)
{
T* p = (T*)::CoTaskMemRealloc(m_pCoMemAlloc, sizeof(T)*(ulNewQueueSize+1));
if (p == NULL)
{
hr = E_OUTOFMEMORY;
}
else
{
m_pCoMemAlloc = p;
if (m_ulHeader >= m_ulTail)
{
//We don't need to do anything.
}
else
{
//data are rounded from end of the queue to the beginning of the queue, we need to move the data to the end of the queue
memmove(m_pCoMemAlloc + ulNewQueueSize + 1 - (m_ulQueueSize - m_ulTail), m_pCoMemAlloc + m_ulTail, (m_ulQueueSize - m_ulTail)*sizeof(T));
m_ulTail += ulNewQueueSize + 1 - m_ulQueueSize;
}
m_ulQueueSize = ulNewQueueSize + 1;
}
}
else if (ulNewQueueSize < m_ulQueueSize-1)
{
if (m_ulQueueSize-1-ulNewQueueSize > _SpaceSize())
{
//This could cause loss of data
hr = E_INVALIDARG;
}
else
{
T* p = (T*)::CoTaskMemAlloc(sizeof(T) * (ulNewQueueSize+1));
if (!p)
{
hr = E_OUTOFMEMORY;
}
else
{
if (m_ulHeader > m_ulTail)
{
//continous memory
memcpy(p, m_pCoMemAlloc + m_ulTail, (m_ulHeader-m_ulTail)*sizeof(T));
}
else if (m_ulHeader < m_ulTail)
{
//copy the memory from m_ulTail to the end of the memory block
memcpy(p, m_pCoMemAlloc + m_ulTail, (m_ulQueueSize - m_ulTail)*sizeof(T));
if (m_ulHeader)
{
memcpy(p + (m_ulQueueSize - m_ulTail), m_pCoMemAlloc, m_ulHeader*sizeof(T));
}
}
m_ulHeader = _DataSize();
m_ulTail = 0;
::CoTaskMemFree(m_pCoMemAlloc);
m_pCoMemAlloc = p;
m_ulQueueSize = ulNewQueueSize + 1;
}
}
}
return hr;
}
template <class T>
HRESULT CBasicQueueByArray<T>::Init(ULONG ulQueueSize, HANDLE hSpaceAvailable, HANDLE hDataAvailable, ULONG ulSpaceNotifySize, ULONG ulDataNotifySize)
{
CRITICAL_SECTION_AUTO_LOCK csl(this);
HRESULT hr = S_OK;
if (ulSpaceNotifySize + ulDataNotifySize >= ulQueueSize)
{
//This could cause deadlock between client thread and SAPI read/write thread
return E_INVALIDARG;
}
_Clear();
if (ulQueueSize)
{
//We allocated additional dummy space is to deal with the case that m_ulHeader catches up from behind to m_ulTail.
//We shouldn't use the dummy space to store any value
m_pCoMemAlloc = (T*)::CoTaskMemAlloc(sizeof(T)*(ulQueueSize + 1));
if (m_pCoMemAlloc)
{
m_ulQueueSize = ulQueueSize + 1;
}
else
{
hr = E_OUTOFMEMORY;
}
}
m_hSpaceAvailable = hSpaceAvailable;
m_hDataAvailable = hDataAvailable;
m_ulSpaceNotifySize = ulSpaceNotifySize;
m_ulDataNotifySize = ulDataNotifySize;
return hr;
}
template <class T>
ULONG CBasicQueueByArray<T>::QueueSize()
{
CRITICAL_SECTION_AUTO_LOCK csl(this);
return m_ulQueueSize ? m_ulQueueSize -1 : 0;
}
template <class T>
void CBasicQueueByArray<T>::InsertHead(T* pElements, ULONG ulCount, ULONG * pulReturnCount)
{
CRITICAL_SECTION_AUTO_LOCK csl(this);
ULONG ulDataBeforeInsert= _DataSize(); //get the available data before we do the update
ULONG ulEmpty = _SpaceSize();
ULONG ulPushCount = (ulEmpty >= ulCount) ? ulCount : ulEmpty;
if (ulPushCount <= (m_ulQueueSize - m_ulHeader))
{
memcpy(m_pCoMemAlloc + m_ulHeader, pElements, sizeof(T)*ulPushCount);
}
else
{
memcpy(m_pCoMemAlloc + m_ulHeader, pElements, sizeof(T)*(m_ulQueueSize - m_ulHeader));
memcpy(m_pCoMemAlloc, pElements + m_ulQueueSize - m_ulHeader, sizeof(T)*(ulPushCount - m_ulQueueSize + m_ulHeader));
}
m_ulHeader = (m_ulHeader + ulPushCount) % m_ulQueueSize;
if (pulReturnCount)
{
*pulReturnCount = ulPushCount;
}
m_ullTotalIn += ulPushCount;
if (ulDataBeforeInsert < m_ulDataNotifySize && m_ulDataNotifySize <= _DataSize())
{
::SetEvent(m_hDataAvailable);
}
}
//When ulCount == 0, the caller wants to get the remaining
template <class T>
void CBasicQueueByArray<T>::RemoveTail(T* pElements, ULONG ulCount, ULONG * pulReturnCount)
{
CRITICAL_SECTION_AUTO_LOCK csl(this);
ULONG ulSpaceBeforeRemove = _SpaceSize(); //get the available space before we do the update
ULONG ulOccupied = _DataSize();
ULONG ulPopCount = (ulOccupied >= ulCount && ulCount) ? ulCount : ulOccupied;
if (ulPopCount <= (m_ulQueueSize - m_ulTail))
{
memcpy(pElements, m_pCoMemAlloc + m_ulTail, sizeof(T)*ulPopCount);
}
else
{
memcpy(pElements, m_pCoMemAlloc + m_ulTail, sizeof(T)*(m_ulQueueSize - m_ulTail));
memcpy(pElements + m_ulQueueSize - m_ulTail, m_pCoMemAlloc, sizeof(T)*(ulPopCount - m_ulQueueSize + m_ulTail));
}
m_ulTail = (m_ulTail + ulPopCount) % m_ulQueueSize;
if (pulReturnCount)
{
*pulReturnCount = ulPopCount;
}
m_ullTotalOut += ulPopCount;
if (ulSpaceBeforeRemove < m_ulSpaceNotifySize && m_ulSpaceNotifySize <= _SpaceSize())
{
::SetEvent(m_hSpaceAvailable);
}
}
template <class T>
void CBasicQueueByArray<T>::ResetPos()
{
CRITICAL_SECTION_AUTO_LOCK csl(this);
m_ulHeader = 0;
m_ulTail = 0;
m_ullTotalOut = 0;
m_ullTotalIn = 0;
}
/****************************************************************************
* SpAudioPlug::Init *
*------------------------------*
* Description:
****************************************************************************/
HRESULT SpAudioPlug::Init(VARIANT_BOOL fWrite, SpeechAudioFormatType FormatType)
{
SPAUTO_OBJ_LOCK;
HRESULT hr = S_OK;
if (fWrite == VARIANT_TRUE)
{
m_fWrite = TRUE;
}
else
{
m_fWrite = FALSE;
}
SPSTREAMFORMAT eFormat = (SPSTREAMFORMAT)FormatType;
if (eFormat == SPSF_Text)
{
return E_INVALIDARG;
}
if (!m_autohAPIEvent)
{
m_autohAPIEvent = CreateEvent(NULL, TRUE, m_fWrite, NULL);
}
if (m_State != SPAS_CLOSED)
{
hr = SPERR_DEVICE_BUSY;
}
else
{
if (eFormat != SPSF_NoAssignedFormat && m_Format.ComputeFormatEnum() != eFormat)
{
hr = m_Format.AssignFormat(eFormat);
if (SUCCEEDED(hr))
{
static const SPAUDIOBUFFERINFO BuffInfo = {50, 500, 0};
hr = SetBufferInfo(&BuffInfo);
}
}
m_fautohAPIEventSet = m_fWrite;
}
return hr;
}
/****************************************************************************
* SpAudioPlug::SetData *
*------------------------------*
* Description:
****************************************************************************/
HRESULT SpAudioPlug::SetData(VARIANT vData, long * pWritten)
{
HRESULT hr = S_OK;
//The method can only be called when the audio object is set to be input device.
if (m_fWrite || m_State != SPAS_RUN)
{
return STG_E_ACCESSDENIED;
}
else
{
bool fByRef = false;
switch (vData.vt)
{
case (VT_ARRAY | VT_BYREF | VT_UI1):
fByRef = true;
break;
case (VT_ARRAY | VT_UI1):
break;
default:
return E_INVALIDARG;
}
BYTE *pArray = NULL;
ULONG ulDataSize = 0;
hr = SafeArrayAccessData( fByRef ? *vData.pparray : vData.parray,
(void **)&pArray );
if( SUCCEEDED( hr ) )
{
ulDataSize = fByRef ?
(*vData.pparray)->rgsabound[0].cElements :
vData.parray->rgsabound[0].cElements;
}
ULONG cbRemaining = ulDataSize;
ULONG ulWrite = 0;
BYTE * pHeader = pArray;
if (pWritten)
{
*pWritten = 0;
}
while(cbRemaining)
{
m_Queue.InsertHead(pHeader, cbRemaining, &ulWrite);
_ProcessEvent();
cbRemaining -= ulWrite;
pHeader += ulWrite;
if (pWritten)
{
*pWritten += ulWrite;
}
if (ulWrite == 0)
{
DWORD dwReturn = ::WaitForSingleObject(m_hQueueHasSpaceEvent, INFINITE);
if (dwReturn == WAIT_OBJECT_0)
{
//It could be signalled by SetState thread, or Read thread
if (m_State == SPAS_CLOSED)
{
//Signalled by SetState thread
break;
}
else
{
//Signalled by Read thread
}
}
}
}
SafeArrayUnaccessData( fByRef ? *vData.pparray : vData.parray);
}
return hr;
}
/****************************************************************************
* SpAudioPlug::GetData *
*------------------------------*
* Description:
****************************************************************************/
HRESULT SpAudioPlug::GetData(VARIANT* pData)
{
HRESULT hr = S_OK;
//The method can only be called when the audio object is set to be output device.
if (!m_fWrite)
{
return STG_E_ACCESSDENIED;
}
//We can lock the queue several times on the same thread without blocking the client thread, this is character of critical section
//The reason we want to lock the queue explicitly is the possible inconsistent state between
m_Queue.Lock();
ULONG ulDataAvailable = m_Queue.DataSize();
if (ulDataAvailable)
{
BYTE *pArray = NULL;
SAFEARRAY* psa = SafeArrayCreateVector( VT_UI1, 0, ulDataAvailable);
if( psa )
{
if( SUCCEEDED( hr = SafeArrayAccessData( psa, (void **)&pArray) ) )
{
m_Queue.RemoveTail(pArray, ulDataAvailable, NULL);
SafeArrayUnaccessData( psa );
VariantClear( pData );
pData->vt = VT_ARRAY | VT_UI1;
pData->parray = psa;
}
else
{
// Free our memory if we failed.
hr = SafeArrayDestroy(psa);
VariantClear( pData );
}
}
else
{
hr = E_OUTOFMEMORY;
}
}
else
{
VariantClear( pData );
hr = S_FALSE;
}
_ProcessEvent();
m_Queue.Unlock();
return hr;
}
/****************************************************************************
* SpAudioPlug::SpAudioPlug *
*------------------------------*
* Description:
* ctor
*********************************************************************/
SpAudioPlug::SpAudioPlug() :
m_State(SPAS_CLOSED),
m_fWrite(TRUE),
m_SpEventSource(this),
m_cbEventBias(0),
m_ulBufferNotifySize(0),
m_hQueueHasDataEvent(NULL),
m_hQueueHasSpaceEvent(NULL),
m_autohAPIEvent(NULL)
{
InitializeCriticalSection(&m_CriticalSection);
}
/****************************************************************************
* SpAudioPlug::FinalConstruct *
*-----------------------------*
* Description:
* Called by ATL when our object is constructed.
************************************************************************/
HRESULT SpAudioPlug::FinalConstruct()
{
HRESULT hr = S_OK;
hr = m_Format.AssignFormat(SPSF_22kHz16BitMono);
if (SUCCEEDED(hr))
{
m_hQueueHasDataEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
}
if (SUCCEEDED(hr))
{
m_hQueueHasSpaceEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
}
static const SPAUDIOBUFFERINFO BuffInfo = {50, 500, 0};
hr = SetBufferInfo(&BuffInfo);
return hr;
}
/****************************************************************************
* SpAudioPlug::FinalRelease *
*-----------------------------*
* Description:
* Called by ATL when our object is going away.
*********************************************************************/
void SpAudioPlug::FinalRelease()
{
if (m_hQueueHasDataEvent)
{
::CloseHandle(m_hQueueHasDataEvent);
}
if (m_hQueueHasSpaceEvent)
{
::CloseHandle(m_hQueueHasSpaceEvent);
}
if (m_autohAPIEvent)
{
CloseHandle(m_autohAPIEvent);
}
DeleteCriticalSection(&m_CriticalSection);
}
/****************************************************************************
* SpAudioPlug::AddEvents *
*-----------------------*
* Description:
* ISpEventSink::AddEvents implementation.
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::AddEvents(const SPEVENT* pEventArray, ULONG ulCount)
{
HRESULT hr = S_OK;
SPAUTO_OBJ_LOCK;
if( SPIsBadReadPtr(pEventArray, sizeof(SPEVENT ) * ulCount))
{
hr = E_INVALIDARG;
}
else
{
ULONGLONG ullDevicePosition = m_fWrite ? m_Queue.GetTotalOut() : m_Queue.GetTotalIn();
hr = m_SpEventSource._AddEvents(pEventArray, ulCount);
m_SpEventSource._CompleteEvents(ullDevicePosition + m_cbEventBias);
}
return hr;
}
/****************************************************************************
* SpAudioPlug::GetEventInterest *
*------------------------------*
* Description:
* ISpEventSink::GetEventInterest implementation.
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::GetEventInterest(ULONGLONG * pullEventInterest)
{
HRESULT hr = S_OK;
SPAUTO_OBJ_LOCK;
if (SP_IS_BAD_WRITE_PTR(pullEventInterest))
{
hr = E_POINTER;
}
else
{
*pullEventInterest = m_SpEventSource.m_ullEventInterest;
}
return hr;
}
/****************************************************************************
* SpAudioPlug::_ProcessEvent *
*---------------------*
* Description:
*
* Return:
*********************************************************************/
void SpAudioPlug::_ProcessEvent()
{
BOOL fSetEvent;
if (m_fWrite)
{
if (m_Queue.DataSize() < m_ulBufferNotifySize)
{
fSetEvent = TRUE;
}
else
{
fSetEvent = FALSE;
}
}
else
{
if (m_Queue.DataSize() >= m_ulBufferNotifySize)
{
fSetEvent = TRUE;
}
else
{
fSetEvent = FALSE;
}
}
if (fSetEvent != m_fautohAPIEventSet)
{
if (fSetEvent)
{
SetEvent(m_autohAPIEvent);
}
else
{
ResetEvent(m_autohAPIEvent);
}
m_fautohAPIEventSet = fSetEvent;
}
ULONGLONG ullDevicePosition = m_fWrite ? m_Queue.GetTotalOut() : m_Queue.GetTotalIn();
m_SpEventSource._CompleteEvents(ullDevicePosition + m_cbEventBias);
}
/****************************************************************************
* SpAudioPlug::Read *
*---------------------*
* Description:
* ISequentialStream::Read implementation.
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::Read(void * pv, ULONG cb, ULONG *pcbRead)
{
HRESULT hr = S_OK;
if (SPIsBadWritePtr(pv, cb) ||
SP_IS_BAD_OPTIONAL_WRITE_PTR(pcbRead))
{
return E_POINTER;
}
if (m_fWrite)
{
return STG_E_ACCESSDENIED;
}
if (pcbRead)
{
*pcbRead = 0;
}
ULONG cbRemaining = cb;
BYTE *pTail = (BYTE*)pv;
ULONG ulRead = 0;
while(cbRemaining)
{
m_Queue.RemoveTail(pTail, cbRemaining, &ulRead);
_ProcessEvent();
if (ulRead)
{
cbRemaining -= ulRead;
pTail += ulRead;
if (pcbRead)
{
*pcbRead += ulRead;
}
}
else
{
if (m_State == SPAS_RUN)
{
DWORD dwReturn = ::WaitForSingleObject(m_hQueueHasDataEvent, 1000);
if (dwReturn == WAIT_OBJECT_0)
{
//Signalled by SetData thread
}
else if (dwReturn == WAIT_TIMEOUT)
{
hr = SPERR_AUDIO_BUFFER_UNDERFLOW;
break;
}
}
else
{
break;
}
}
}
return hr;
}
/****************************************************************************
* SpAudioPlug::Write *
*----------------------*
* Description:
* ISequentialStream::Write implementation.
*
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::Write(const void * pv, ULONG cb, ULONG *pcb)
{
HRESULT hr = S_OK;
if (!m_fWrite)
{
return STG_E_ACCESSDENIED;
}
else if (m_State != SPAS_RUN && m_State != SPAS_PAUSE)
{
return SPERR_AUDIO_STOPPED;
}
else
{
ULONG cbRemaining = cb;
ULONG ulWrite = 0;
BYTE * pHeader = (BYTE*)pv;
while(cbRemaining)
{
m_Queue.InsertHead(pHeader, cbRemaining, &ulWrite);
_ProcessEvent();
cbRemaining -= ulWrite;
pHeader += ulWrite;
if (pcb)
{
*pcb += ulWrite;
}
if (ulWrite == 0)
{
DWORD dwReturn = ::WaitForSingleObject(m_hQueueHasSpaceEvent, INFINITE);
if (dwReturn == WAIT_OBJECT_0)
{
//It could be signalled by SetState thread, or GetData thread
if (m_State == SPAS_CLOSED)
{
//Signalled by SetState thread
break;
}
else
{
//Signalled by GetData thread
}
}
}
}
}
return hr;
}
/****************************************************************************
* SpAudioPlug::Seek *
*---------------------*
* Description:
* IStream::Seek implementation. It can only be used to retrieve the current seek position
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER * plibNewPosition)
{
SPAUTO_OBJ_LOCK;
HRESULT hr = S_OK;
if (dwOrigin != STREAM_SEEK_CUR || dlibMove.QuadPart)
{
hr = E_INVALIDARG;
}
else
{
if (SPIsBadWritePtr(plibNewPosition, sizeof(*plibNewPosition)))
{
hr = E_POINTER;
}
else
{
ULONGLONG ullSeekPosition = m_fWrite ? m_Queue.GetTotalIn() : m_Queue.GetTotalOut();
plibNewPosition->QuadPart = ullSeekPosition;
}
}
return hr;
}
/****************************************************************************
* SpAudioPlug::SetSize *
*------------------------*
* Description:
* IStream::SetSize implementation.
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::SetSize(ULARGE_INTEGER libNewSize)
{
SPAUTO_OBJ_LOCK;
return m_Queue.Resize(libNewSize.LowPart);
}
/****************************************************************************
* SpAudioPlug::CopyTo *
*-----------------------*
* Description:
* IStream::CopyTo implementation. Delegate to the actual audio device.
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::CopyTo(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
{
SPAUTO_OBJ_LOCK;
if (m_fWrite)
{
return STG_E_ACCESSDENIED;
}
else
{
return E_NOTIMPL;
}
}
/****************************************************************************
* SpAudioPlug::Commit *
*-----------------------*
* Description:
* IStream::Commit implementation. Delegate to the actual audio device.
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::Commit(DWORD grfCommitFlags)
{
HRESULT hr = S_OK;
SPAUTO_OBJ_LOCK;
if (m_fWrite && m_State == SPAS_RUN)
{
_ProcessEvent(); // Call this to clear the event if we're writing
}
return hr;
}
/****************************************************************************
* SpAudioPlug::Revert *
*-----------------------*
* Description:
* IStream::Revert implementation. Delegate to the actual audio device.
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::Revert(void)
{
SPAUTO_OBJ_LOCK;
return E_NOTIMPL;
}
/****************************************************************************
* SpAudioPlug::LockRegion *
*---------------------------*
* Description:
* IStream::LockRegion implementation. Delegate to the actual audio
* device.
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
{
SPAUTO_OBJ_LOCK;
return E_NOTIMPL;
}
/****************************************************************************
* SpAudioPlug::UnlockRegion *
*-----------------------------*
* Description:
* IStream::UnlockRegion implementation. Delegate to the actual audio
* device.
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
{
SPAUTO_OBJ_LOCK;
return E_NOTIMPL;
}
/****************************************************************************
* SpAudioPlug::Stat *
*---------------------*
* Description:
* IStream::Stat implementation. Delegate to the actual audio device.
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
{
SPAUTO_OBJ_LOCK;
return E_NOTIMPL;
}
/****************************************************************************
* SpAudioPlug::Clone *
*----------------------*
* Description:
* IStream::Clone implementation. Delegate to the actual audio device.
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::Clone(IStream **ppstm)
{
SPAUTO_OBJ_LOCK;
return E_NOTIMPL;
}
/****************************************************************************
* SpAudioPlug::GetFormat *
*--------------------------*
* Description:
* ISpStreamFormat::GetFormat implementation.
* GetFormat is called for input device
*
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::GetFormat(GUID * pguidFormatId, WAVEFORMATEX ** ppCoMemWaveFormatEx)
{
HRESULT hr = S_OK;
SPAUTO_OBJ_LOCK;
if (SP_IS_BAD_WRITE_PTR(pguidFormatId) ||
SP_IS_BAD_WRITE_PTR(ppCoMemWaveFormatEx))
{
return E_POINTER;
}
//m_guidFormatId and m_pCoMemWaveFormatEx need to be initialized by ISpAudioPlug::Init
if (m_Format.FormatId() == GUID_NULL || m_Format.WaveFormatExPtr() == NULL)
{
hr = SPERR_UNINITIALIZED;
}
else
{
hr = m_Format.CopyTo(pguidFormatId, ppCoMemWaveFormatEx);
}
return hr;
}
/****************************************************************************
* SpAudioPlug::SetState *
*-------------------------*
* Description:
* ISpAudio::SetState implementation.
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::SetState(SPAUDIOSTATE NewState, ULONGLONG ullReserved )
{
HRESULT hr = S_OK;
SPAUTO_OBJ_LOCK;
if (m_State != NewState)
{
m_State = NewState;
if (NewState != SPAS_RUN)
{
if (NewState == SPAS_STOP || NewState == SPAS_CLOSED)
{
m_Queue.ResetPos();
}
SetEvent(m_hQueueHasDataEvent); //Signal the read/getdata thread it exit
SetEvent(m_hQueueHasSpaceEvent); //Signal the write/setdata thread it exit
}
}
return hr;
}
/****************************************************************************
* SpAudioPlug::SetFormat *
*--------------------------*
* Description:
* ISpAudio::SetFormat implementation. We don't allow setting the format
* to anything other than the input format.
* We'll let the format converter do the right thing for us for
* the SR engine.
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::SetFormat(REFGUID rguidFmtId, const WAVEFORMATEX * pWaveFormatEx)
{
HRESULT hr = S_OK;
SPAUTO_OBJ_LOCK;
if (!m_Format.IsEqual(rguidFmtId, pWaveFormatEx))
{
hr = SPERR_UNSUPPORTED_FORMAT;
}
return hr;
}
/****************************************************************************
* SpAudioPlug::GetStatus *
*--------------------------*
* Description:
* ISpAudio::GetStatus implementation.
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::GetStatus(SPAUDIOSTATUS *pStatus)
{
SPAUTO_OBJ_LOCK;
HRESULT hr = S_OK;
if (SP_IS_BAD_WRITE_PTR(pStatus))
{
hr = E_POINTER;
}
else
{
//Because there are more than one operation on m_Queue, we need to lock the queue explicitly
m_Queue.Lock();
if (m_State == SPAS_RUN)
{
long lDataInQueue = (long)(m_Queue.DataSize());
pStatus->cbFreeBuffSpace = ((long)m_Queue.QueueSize()) - lDataInQueue;
if (m_fWrite)
{
pStatus->cbNonBlockingIO = pStatus->cbFreeBuffSpace;
}
else
{
pStatus->cbNonBlockingIO = lDataInQueue;
}
}
else
{
pStatus->cbFreeBuffSpace = 0;
pStatus->cbNonBlockingIO = 0;
}
pStatus->CurSeekPos = m_fWrite ? m_Queue.GetTotalIn() : m_Queue.GetTotalOut();
pStatus->State = m_State;
pStatus->CurDevicePos = m_fWrite ? m_Queue.GetTotalOut() : m_Queue.GetTotalIn();;
pStatus->dwAudioLevel = 0;
pStatus->dwReserved2 = 0;
m_Queue.Unlock();
}
return hr;
}
/****************************************************************************
* SpAudioPlug::SetBufferInfo *
*------------------------------*
* Description:
* ISpAudio::SetBufferInfo implementation. Delegate to the actual audio
* device.
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::SetBufferInfo(const SPAUDIOBUFFERINFO * pInfo)
{
SPAUTO_OBJ_LOCK;
HRESULT hr = S_OK;
if (m_State != SPAS_CLOSED)
{
hr = SPERR_DEVICE_BUSY;
}
else
{
m_BufferInfo = *pInfo;
if (m_fWrite)
{
m_Queue.Init(m_Format.WaveFormatExPtr()->nAvgBytesPerSec * pInfo->ulMsBufferSize / 1000,
m_hQueueHasSpaceEvent,
m_hQueueHasDataEvent,
m_Format.WaveFormatExPtr()->nAvgBytesPerSec *pInfo->ulMsMinNotification / 1000,
1);
}
else
{
m_Queue.Init(m_Format.WaveFormatExPtr()->nAvgBytesPerSec * pInfo->ulMsBufferSize / 1000,
m_hQueueHasSpaceEvent,
m_hQueueHasDataEvent,
1,
1);
}
m_cbEventBias = (m_Format.WaveFormatExPtr()->nAvgBytesPerSec * pInfo->ulMsEventBias) / 1000;
}
return hr;
}
/****************************************************************************
* SpAudioPlug::GetBufferInfo *
*------------------------------*
* Description:
* ISpAudio::GetBufferInfo implementation. Delegate to the actual audio
* device.
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::GetBufferInfo(SPAUDIOBUFFERINFO * pInfo)
{
HRESULT hr = S_OK;
SPAUTO_OBJ_LOCK;
*pInfo = m_BufferInfo;
return hr;
}
/****************************************************************************
* SpAudioPlug::GetDefaultFormat *
*---------------------------------*
* Description:
* ISpAudio::GetDefaultFormat implementation.
* GetDefaultFormat is called for output device.
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::GetDefaultFormat(GUID * pFormatId, WAVEFORMATEX ** ppCoMemWaveFormatEx)
{
HRESULT hr = S_OK;
SPAUTO_OBJ_LOCK;
if (SP_IS_BAD_WRITE_PTR(pFormatId) ||
SP_IS_BAD_WRITE_PTR(ppCoMemWaveFormatEx))
{
return E_POINTER;
}
//m_guidFormatId and m_pCoMemWaveFormatEx need to be initialized by ISpAudioPlug::Init
if (m_Format.FormatId() == GUID_NULL || m_Format.WaveFormatExPtr() == NULL)
{
hr = SPERR_UNINITIALIZED;
}
else
{
hr = m_Format.CopyTo(pFormatId, ppCoMemWaveFormatEx);
}
return hr;
}
/****************************************************************************
* SpAudioPlug::EventHandle *
*----------------------------*
* Description:
* ISpAudio::EventHandle implementation. Delegate to the actual audio
* device.
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP_(HANDLE) SpAudioPlug::EventHandle()
{
SPAUTO_OBJ_LOCK;
return m_autohAPIEvent;
}
/****************************************************************************
* SpAudioPlug::GetVolumeLevel *
*-------------------------------*
* Description:
* ISpAudio:GetVolumeLevel implementation. Delegate to the actual audio
* device.
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::GetVolumeLevel(ULONG *pLevel)
{
SPAUTO_OBJ_LOCK;
return E_NOTIMPL ;
}
/****************************************************************************
* SpAudioPlug::SetVolumeLevel *
*-------------------------------*
* Description:
* ISpAudio::SetVolumeLevel implementation. Delegate to the actual audio
* device.
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::SetVolumeLevel(ULONG Level)
{
SPAUTO_OBJ_LOCK;
return E_NOTIMPL ;
}
/****************************************************************************
* SpAudioPlug::GetBufferNotifySize *
*------------------------------------*
* Description:
* ISpAudio::GetBufferNotifySize implementation. Delegate to the actual
* audio device.
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::GetBufferNotifySize(ULONG *pcbSize)
{
HRESULT hr = S_OK;
SPAUTO_OBJ_LOCK;
*pcbSize = m_ulBufferNotifySize;
return hr;
}
/****************************************************************************
* SpAudioPlug::SetBufferNotifySize *
*------------------------------------*
* Description:
* ISpAudio::SetBufferNotifySize implementation. Delegate to the actual
* audio device.
*
* Return:
* S_OK on success
* FAILED(hr) otherwise
*********************************************************************/
STDMETHODIMP SpAudioPlug::SetBufferNotifySize(ULONG cbSize)
{
HRESULT hr = S_OK;
SPAUTO_OBJ_LOCK;
m_ulBufferNotifySize = cbSize;
return hr;
}