// 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 void CBasicQueueByArray::_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 ULONG CBasicQueueByArray::_SpaceSize() { return (m_ulHeader >= m_ulTail) ? (m_ulQueueSize - (m_ulHeader - m_ulTail) - 1) : (m_ulTail - m_ulHeader - 1); } template ULONG CBasicQueueByArray::_DataSize() { return (m_ulHeader >= m_ulTail) ? (m_ulHeader - m_ulTail) : (m_ulQueueSize - (m_ulTail - m_ulHeader)); } template CBasicQueueByArray::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 CBasicQueueByArray::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 HRESULT CBasicQueueByArray::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 HRESULT CBasicQueueByArray::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 ULONG CBasicQueueByArray::QueueSize() { CRITICAL_SECTION_AUTO_LOCK csl(this); return m_ulQueueSize ? m_ulQueueSize -1 : 0; } template void CBasicQueueByArray::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 void CBasicQueueByArray::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 void CBasicQueueByArray::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; }