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.
605 lines
18 KiB
605 lines
18 KiB
/******************************************************************************* |
|
* SPEventQ.h * |
|
*------------* |
|
* Description: |
|
* This is the header file for the SAPI5 event queue implementation. |
|
*------------------------------------------------------------------------------- |
|
* Copyright (c) Microsoft Corporation. All rights reserved. |
|
*******************************************************************************/ |
|
#ifndef SPEventQ_h |
|
#define SPEventQ_h |
|
|
|
#ifndef SPHelper_h |
|
#include <SPHelper.h> |
|
#endif |
|
|
|
#ifndef SPCollec_h |
|
#include <SPCollec.h> |
|
#endif |
|
|
|
//=== Inline helpers for copying and deleting events ============================ |
|
|
|
|
|
//=== Class definition ========================================================== |
|
|
|
class CSpEventNode : public CSpEvent |
|
{ |
|
public: |
|
CSpEventNode * m_pNext; |
|
static LONG Compare(const CSpEventNode * p1, const CSpEventNode *p2) |
|
{ |
|
// Assumes offsets DO or DO NOT reset when stream number changes |
|
if (p1->ulStreamNum < p2->ulStreamNum) |
|
{ |
|
return -1; |
|
} |
|
else if (p1->ulStreamNum > p2->ulStreamNum) |
|
{ |
|
return 1; |
|
} |
|
else if (p1->ullAudioStreamOffset < p2->ullAudioStreamOffset) |
|
{ |
|
return -1; |
|
} |
|
else if (p1->ullAudioStreamOffset > p2->ullAudioStreamOffset) |
|
{ |
|
return 1; |
|
} |
|
return 0; |
|
} |
|
}; |
|
|
|
|
|
typedef CSpBasicQueue<CSpEventNode, TRUE, TRUE> CSpEventList; |
|
|
|
#define DECLARE_SPNOTIFYSOURCE_METHODS(T) \ |
|
STDMETHODIMP SetNotifySink(ISpNotifySink * pNotifySink) \ |
|
{ return T._SetNotifySink(pNotifySink); } \ |
|
STDMETHODIMP SetNotifyWindowMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) \ |
|
{ return T._SetNotifyWindowMessage(hWnd, Msg, wParam, lParam); } \ |
|
STDMETHODIMP SetNotifyCallbackFunction(SPNOTIFYCALLBACK * pfnCallback, WPARAM wParam, LPARAM lParam) \ |
|
{ return T._SetNotifyCallbackFunction(pfnCallback, wParam, lParam); } \ |
|
STDMETHODIMP SetNotifyCallbackInterface(ISpNotifyCallback * pSpCallback, WPARAM wParam, LPARAM lParam) \ |
|
{ return T._SetNotifyCallbackInterface(pSpCallback, wParam, lParam); } \ |
|
STDMETHODIMP SetNotifyWin32Event() \ |
|
{ return T._SetNotifyWin32Event(); } \ |
|
STDMETHODIMP WaitForNotifyEvent(DWORD dwMilliseconds) \ |
|
{ return T._WaitForNotifyEvent(dwMilliseconds); } \ |
|
STDMETHODIMP_(HANDLE) GetNotifyEventHandle() \ |
|
{ return T._GetNotifyEventHandle(); } |
|
|
|
#define DECLARE_SPEVENTSOURCE_METHODS(T) \ |
|
DECLARE_SPNOTIFYSOURCE_METHODS(T) \ |
|
STDMETHODIMP SetInterest(ULONGLONG ullEventInterest, ULONGLONG ullQueuedInterest) \ |
|
{ return T._SetInterest(ullEventInterest, ullQueuedInterest); } \ |
|
STDMETHODIMP GetEvents(ULONG ulCount, SPEVENT* pEventArray, ULONG * pulFetched) \ |
|
{ return T._GetEvents(ulCount, pEventArray, pulFetched); } \ |
|
STDMETHODIMP GetInfo(SPEVENTSOURCEINFO *pInfo) \ |
|
{ return T._GetInfo(pInfo); } |
|
|
|
|
|
|
|
class CSpEventSource |
|
{ |
|
public: |
|
CSpEventSource(CComObjectRootEx<CComMultiThreadModel> * pParent) : |
|
m_pParent(pParent) |
|
{ |
|
m_ullEventInterest = 0; m_ullQueuedInterest = 0; |
|
m_ulStreamNum = 0; |
|
} |
|
HRESULT _SetNotifySink(ISpNotifySink * pNotifySink); |
|
HRESULT _SetNotifyWindowMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); |
|
HRESULT _SetNotifyCallbackFunction(SPNOTIFYCALLBACK * pfnCallback, WPARAM wParam, LPARAM lParam); |
|
HRESULT _SetNotifyCallbackInterface(ISpNotifyCallback * pSpCallback, WPARAM wParam, LPARAM lParam); |
|
HRESULT _SetNotifyWin32Event(); |
|
HRESULT _WaitForNotifyEvent(DWORD dwMilliseconds); |
|
HANDLE _GetNotifyEventHandle(); |
|
|
|
HRESULT _SetInterest(ULONGLONG ullEventInterest , ULONGLONG ullQueuedInterest); |
|
HRESULT _GetEvents( ULONG ulCount, SPEVENT* pEventArray, ULONG * pulFetched ); |
|
HRESULT _GetInfo(SPEVENTSOURCEINFO *pInfo ); |
|
|
|
/*--- Non interface methods ---*/ |
|
HRESULT _CompleteEvents( ULONGLONG ullPos = 0xFFFFFFFFFFFFFFFF ); |
|
inline void _MoveAllToFreeList(CSpEventList * pList); |
|
inline void _RemoveAllEvents(); |
|
inline HRESULT _AddEvent(const SPEVENT & Event); |
|
inline HRESULT _AddEvents(const SPEVENT* pEventArray, ULONG ulCount); |
|
inline HRESULT _DeserializeAndAddEvent(const BYTE * pBuffer, ULONG * pcbUsed); |
|
inline HRESULT _GetStreamNumber(const ULONGLONG ullAudioOffset, ULONG *pulStreamNum); |
|
//=== Data members ============================== |
|
public: |
|
ULONGLONG m_ullEventInterest; |
|
ULONGLONG m_ullQueuedInterest; |
|
ULONG m_ulStreamNum; |
|
CSpEventList m_PendingList; |
|
CSpEventList m_CompletedList; |
|
CSpEventList m_FreeList; |
|
CComPtr<ISpNotifySink> m_cpNotifySink; |
|
CComPtr<ISpNotifyTranslator> m_cpEventTranslator; // If non-NULL then Win32 events being used |
|
CComObjectRootEx<CComMultiThreadModel> * m_pParent; |
|
CComAutoCriticalSection m_NotifyObjChangeCrit; // Critical section used to make sure that |
|
// the notify object (m_cpNotifySink) not changed |
|
// while waiting on it. |
|
|
|
}; |
|
|
|
|
|
// |
|
//=== Inlines ========================================================= |
|
// |
|
|
|
// |
|
// WARNING: If this logic changes, you will need to change the logic in SetNotifyWin32Event also. |
|
// |
|
inline HRESULT CSpEventSource::_SetNotifySink(ISpNotifySink * pNotifySink) |
|
{ |
|
if (SP_IS_BAD_OPTIONAL_INTERFACE_PTR(pNotifySink)) |
|
{ |
|
return E_INVALIDARG; |
|
} |
|
else |
|
{ |
|
m_pParent->Lock(); |
|
m_NotifyObjChangeCrit.Lock(); |
|
m_cpEventTranslator.Release(); |
|
m_cpNotifySink = pNotifySink; |
|
if (m_cpNotifySink && m_CompletedList.GetHead()) |
|
{ |
|
m_cpNotifySink->Notify(); |
|
} |
|
m_NotifyObjChangeCrit.Unlock(); |
|
m_pParent->Unlock(); |
|
return S_OK; |
|
} |
|
} |
|
|
|
/**************************************************************************** |
|
* CSpEventSource::_SetNotifyWindowMessage * |
|
*-----------------------------------------* |
|
* Description: |
|
* |
|
* Returns: |
|
* |
|
********************************************************************* RAL ***/ |
|
|
|
inline HRESULT CSpEventSource::_SetNotifyWindowMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) |
|
{ |
|
SPDBG_FUNC("CSpEventSource::_SetNotifyWindowMessage"); |
|
HRESULT hr = S_OK; |
|
CComPtr<ISpNotifyTranslator> cpTranslator; |
|
hr = cpTranslator.CoCreateInstance(CLSID_SpNotifyTranslator); |
|
if (SUCCEEDED(hr)) |
|
{ |
|
hr = cpTranslator->InitWindowMessage(hWnd, Msg, wParam, lParam); |
|
} |
|
if (SUCCEEDED(hr)) |
|
{ |
|
hr = _SetNotifySink(cpTranslator); |
|
} |
|
return hr; |
|
} |
|
/**************************************************************************** |
|
* CSpEventSource::_SetNotifyCallbackFunction * |
|
*--------------------------------------------* |
|
* Description: |
|
* |
|
* Returns: |
|
* |
|
********************************************************************* RAL ***/ |
|
|
|
inline HRESULT CSpEventSource::_SetNotifyCallbackFunction(SPNOTIFYCALLBACK * pfnCallback, WPARAM wParam, LPARAM lParam) |
|
{ |
|
SPDBG_FUNC("CSpEventSource::_SetNotifyCallbackFunction"); |
|
HRESULT hr = S_OK; |
|
CComPtr<ISpNotifyTranslator> cpTranslator; |
|
hr = cpTranslator.CoCreateInstance(CLSID_SpNotifyTranslator); |
|
if (SUCCEEDED(hr)) |
|
{ |
|
hr = cpTranslator->InitCallback(pfnCallback, wParam, lParam); |
|
} |
|
if (SUCCEEDED(hr)) |
|
{ |
|
hr = _SetNotifySink(cpTranslator); |
|
} |
|
return hr; |
|
} |
|
/**************************************************************************** |
|
* CSpEventSource::_SetNotifyCallbackInterface * |
|
*---------------------------------------------* |
|
* Description: |
|
* |
|
* Returns: |
|
* |
|
********************************************************************* RAL ***/ |
|
|
|
inline HRESULT CSpEventSource::_SetNotifyCallbackInterface(ISpNotifyCallback * pSpCallback, WPARAM wParam, LPARAM lParam) |
|
{ |
|
SPDBG_FUNC("CSpEventSource::_SetNotifyCallbackInterface"); |
|
HRESULT hr = S_OK; |
|
CComPtr<ISpNotifyTranslator> cpTranslator; |
|
hr = cpTranslator.CoCreateInstance(CLSID_SpNotifyTranslator); |
|
if (SUCCEEDED(hr)) |
|
{ |
|
hr = cpTranslator->InitSpNotifyCallback(pSpCallback, wParam, lParam); |
|
} |
|
if (SUCCEEDED(hr)) |
|
{ |
|
hr = _SetNotifySink(cpTranslator); |
|
} |
|
return hr; |
|
} |
|
/**************************************************************************** |
|
* CSpEventSource::_SetNotifyWin32Event * |
|
*--------------------------------------* |
|
* Description: |
|
* |
|
* Returns: |
|
* |
|
********************************************************************* RAL ***/ |
|
|
|
inline HRESULT CSpEventSource::_SetNotifyWin32Event(void) |
|
{ |
|
SPDBG_FUNC("CSpEventSource::_SetNotifyWin32Event"); |
|
HRESULT hr = S_OK; |
|
CComPtr<ISpNotifyTranslator> cpTranslator; |
|
hr = cpTranslator.CoCreateInstance(CLSID_SpNotifyTranslator); |
|
if (SUCCEEDED(hr)) |
|
{ |
|
hr = cpTranslator->InitWin32Event(NULL, TRUE); |
|
} |
|
if (SUCCEEDED(hr)) |
|
{ |
|
// |
|
// In this case we do NOT call _SetNotify sink since we want to set the cpEventTranslator |
|
// |
|
m_pParent->Lock(); |
|
m_NotifyObjChangeCrit.Lock(); |
|
m_cpEventTranslator = cpTranslator; |
|
m_cpNotifySink = cpTranslator; |
|
if (m_cpNotifySink && m_CompletedList.GetHead()) |
|
{ |
|
m_cpNotifySink->Notify(); |
|
} |
|
m_NotifyObjChangeCrit.Unlock(); |
|
m_pParent->Unlock(); |
|
} |
|
return hr; |
|
} |
|
/**************************************************************************** |
|
* CSpEventSource::_WaitForNotifyEvent * |
|
*-------------------------------------* |
|
* Description: |
|
* |
|
* Returns: |
|
* |
|
********************************************************************* RAL ***/ |
|
|
|
inline HRESULT CSpEventSource::_WaitForNotifyEvent(DWORD dwMilliseconds) |
|
{ |
|
SPDBG_FUNC("CSpEventSource::_WaitForNotifyEvent"); |
|
HRESULT hr = S_OK; |
|
m_NotifyObjChangeCrit.Lock(); |
|
if (m_cpEventTranslator) |
|
{ |
|
hr = m_cpEventTranslator->Wait(dwMilliseconds); |
|
} |
|
else |
|
{ |
|
if (m_cpNotifySink) |
|
{ |
|
hr = SPERR_ALREADY_INITIALIZED; |
|
} |
|
else |
|
{ |
|
hr = _SetNotifyWin32Event(); |
|
if (SUCCEEDED(hr)) |
|
{ |
|
hr = m_cpEventTranslator->Wait(dwMilliseconds); |
|
} |
|
} |
|
} |
|
m_NotifyObjChangeCrit.Unlock(); |
|
return hr; |
|
} |
|
/**************************************************************************** |
|
* CSpEventSource::_GetNotifyEventHandle * |
|
*---------------------------------------* |
|
* Description: |
|
* |
|
* Returns: |
|
* |
|
********************************************************************* RAL ***/ |
|
|
|
inline HANDLE CSpEventSource::_GetNotifyEventHandle() |
|
{ |
|
HANDLE h = NULL; |
|
SPDBG_FUNC("CSpEventSource::_GetNotifyEventHandle"); |
|
m_NotifyObjChangeCrit.Lock(); |
|
if (!m_cpNotifySink) |
|
{ |
|
_SetNotifyWin32Event(); |
|
} |
|
if (m_cpEventTranslator) |
|
{ |
|
h = m_cpEventTranslator->GetEventHandle(); |
|
} |
|
m_NotifyObjChangeCrit.Unlock(); |
|
return h; |
|
} |
|
|
|
|
|
inline HRESULT CSpEventSource::_SetInterest( ULONGLONG ullEventInterest, ULONGLONG ullQueuedInterest ) |
|
{ |
|
HRESULT hr = S_OK; |
|
m_pParent->Lock(); |
|
|
|
if(ullEventInterest && SPFEI_FLAGCHECK != (ullEventInterest & SPFEI_FLAGCHECK)) |
|
{ |
|
hr = E_INVALIDARG; |
|
} |
|
else if(ullQueuedInterest && SPFEI_FLAGCHECK != (ullQueuedInterest & SPFEI_FLAGCHECK)) |
|
{ |
|
hr = E_INVALIDARG; |
|
} |
|
else if ((ullQueuedInterest | ullEventInterest) != ullEventInterest) |
|
{ |
|
hr = E_INVALIDARG; |
|
} |
|
else |
|
{ |
|
m_ullEventInterest = ullEventInterest; |
|
m_ullQueuedInterest = ullQueuedInterest; |
|
} |
|
m_pParent->Unlock(); |
|
return hr; |
|
} |
|
|
|
|
|
// |
|
// Same as AddEvents except: No param validation, and caller must take the critical section |
|
// prior to calling. |
|
// |
|
inline HRESULT CSpEventSource::_AddEvents( const SPEVENT* pEventArray, ULONG ulCount ) |
|
{ |
|
HRESULT hr = S_OK; |
|
for( ULONG i = 0; i < ulCount && SUCCEEDED(hr = _AddEvent(pEventArray[i])); ++i ) {} |
|
return hr; |
|
} |
|
|
|
inline HRESULT CSpEventSource::_AddEvent(const SPEVENT & Event) |
|
{ |
|
SPDBG_ASSERT(Event.eEventId < 64); |
|
SPDBG_ASSERT(Event.elParamType == SPET_LPARAM_IS_UNDEFINED || |
|
Event.elParamType == SPET_LPARAM_IS_TOKEN || |
|
Event.elParamType == SPET_LPARAM_IS_OBJECT || |
|
Event.elParamType == SPET_LPARAM_IS_POINTER || |
|
Event.elParamType == SPET_LPARAM_IS_STRING); |
|
#ifdef _DEBUG |
|
if (Event.eEventId == SPEI_VOICE_CHANGE) |
|
{ |
|
SPDBG_ASSERT(Event.elParamType == SPET_LPARAM_IS_TOKEN); |
|
} |
|
else if (Event.eEventId == SPEI_RECOGNITION || Event.eEventId == SPEI_FALSE_RECOGNITION || Event.eEventId == SPEI_HYPOTHESIS) |
|
{ |
|
SPDBG_ASSERT(Event.elParamType == SPET_LPARAM_IS_OBJECT); |
|
} |
|
else if (Event.eEventId ==SPEI_REQUEST_UI || Event.eEventId == SPEI_TTS_BOOKMARK) |
|
{ |
|
SPDBG_ASSERT(Event.elParamType == SPET_LPARAM_IS_STRING); |
|
} |
|
#endif |
|
|
|
if ( (1i64 << Event.eEventId) & m_ullEventInterest ) |
|
{ |
|
CSpEventNode *pNode = m_FreeList.RemoveHead(); |
|
if (pNode == NULL) |
|
{ |
|
pNode = new CSpEventNode(); |
|
if (pNode == NULL) |
|
{ |
|
return E_OUTOFMEMORY; |
|
} |
|
} |
|
pNode->CopyFrom(&Event); |
|
m_PendingList.InsertSorted(pNode); |
|
} |
|
return S_OK; |
|
} |
|
|
|
inline HRESULT CSpEventSource:: |
|
_DeserializeAndAddEvent(const BYTE *pBuffer, ULONG * pcbUsed) |
|
{ |
|
HRESULT hr = S_OK; |
|
const SPEVENT * pSrcEvent = (const SPEVENT *)pBuffer; |
|
SPDBG_ASSERT(pSrcEvent->eEventId < 64); |
|
if ( (1i64 << pSrcEvent->eEventId) & m_ullEventInterest ) |
|
{ |
|
CSpEventNode *pNode = m_FreeList.RemoveHead(); |
|
if (pNode == NULL) |
|
{ |
|
pNode = new CSpEventNode(); |
|
if (pNode == NULL) |
|
{ |
|
hr = E_OUTOFMEMORY; |
|
} |
|
} |
|
if (SUCCEEDED(hr)) |
|
{ |
|
hr = pNode->Deserialize(((const SPSERIALIZEDEVENT64 *)(pBuffer)), pcbUsed); |
|
if (SUCCEEDED(hr)) |
|
{ |
|
m_PendingList.InsertSorted(pNode); |
|
} |
|
else |
|
{ |
|
m_FreeList.InsertHead(pNode); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
// WCE compiler does not work propertly with template |
|
#ifndef _WIN32_WCE |
|
*pcbUsed = SpEventSerializeSize<SPSERIALIZEDEVENT64>(pSrcEvent); |
|
#else |
|
*pcbUsed = SpEventSerializeSize(pSrcEvent, sizeof(SPSERIALIZEDEVENT64)); |
|
#endif |
|
} |
|
return hr; |
|
} |
|
|
|
inline HRESULT CSpEventSource::_GetEvents( ULONG ulCount, SPEVENT* pEventArray, ULONG *pulFetched ) |
|
{ |
|
HRESULT hr = S_OK; |
|
m_pParent->Lock(); |
|
if( SPIsBadWritePtr( pEventArray, sizeof( SPEVENT ) * ulCount ) || |
|
SP_IS_BAD_OPTIONAL_WRITE_PTR(pulFetched) ) |
|
{ |
|
hr = E_INVALIDARG; |
|
} |
|
else |
|
{ |
|
ULONG ulCopied = 0; |
|
ULONG ulRemaining = ulCount; |
|
CSpEventNode * pCur = m_CompletedList.m_pHead; |
|
CSpEventNode * pLastCopied = NULL; |
|
while (ulRemaining && pCur) |
|
{ |
|
pCur->Detach(pEventArray + ulCopied); |
|
pLastCopied = pCur; |
|
ulCopied++; |
|
pCur = pCur->m_pNext; |
|
ulRemaining--; |
|
} |
|
if (ulCopied) |
|
{ |
|
if (m_FreeList.m_pHead == NULL) |
|
{ |
|
m_FreeList.m_pTail = pLastCopied; |
|
} |
|
pLastCopied->m_pNext = m_FreeList.m_pHead; |
|
m_FreeList.m_pHead = m_CompletedList.m_pHead; |
|
m_CompletedList.m_pHead = pCur; |
|
m_CompletedList.m_cElements -= ulCopied; |
|
m_FreeList.m_cElements += ulCopied; |
|
} |
|
if (ulCopied < ulCount) |
|
{ |
|
hr = S_FALSE; |
|
} |
|
if (pulFetched) |
|
{ |
|
*pulFetched = ulCopied; |
|
} |
|
} |
|
m_pParent->Unlock(); |
|
return hr; |
|
} |
|
|
|
|
|
inline HRESULT CSpEventSource::_GetInfo( SPEVENTSOURCEINFO * pInfo ) |
|
{ |
|
HRESULT hr = S_OK; |
|
m_pParent->Lock(); |
|
if( SP_IS_BAD_WRITE_PTR( pInfo ) ) |
|
{ |
|
hr = E_POINTER; |
|
} |
|
else |
|
{ |
|
pInfo->ulCount = m_CompletedList.GetCount(); |
|
pInfo->ullEventInterest = m_ullEventInterest; |
|
pInfo->ullQueuedInterest= m_ullQueuedInterest; |
|
} |
|
m_pParent->Unlock(); |
|
return hr; |
|
} |
|
|
|
|
|
|
|
// |
|
// The caller must call this function with the critical section owned |
|
// |
|
inline HRESULT CSpEventSource::_CompleteEvents( ULONGLONG ullPos ) |
|
{ |
|
HRESULT hr = S_OK; |
|
|
|
if (m_PendingList.m_pHead && m_PendingList.m_pHead->ullAudioStreamOffset <= ullPos) |
|
{ |
|
BOOL bNotify = FALSE; |
|
while (m_PendingList.m_pHead && |
|
m_PendingList.m_pHead->ullAudioStreamOffset <= ullPos) |
|
{ |
|
CSpEventNode *pNode = m_PendingList.RemoveHead(); |
|
if(pNode->ulStreamNum != m_ulStreamNum) |
|
{ |
|
m_ulStreamNum = pNode->ulStreamNum; |
|
} |
|
if ( (1i64 << pNode->eEventId) & m_ullEventInterest ) |
|
{ |
|
bNotify = TRUE; |
|
// |
|
// NOTE: If we're forwarding events to an event sink then we'll only |
|
// pay attention to the Interest flags. If we're going to notify, then |
|
// we'll only queue completed events that the user has explicitly asked |
|
// us to store as completed events. |
|
// |
|
if ( (1i64 << pNode->eEventId) & m_ullQueuedInterest ) |
|
{ |
|
m_CompletedList.InsertSorted(pNode); |
|
} |
|
else |
|
{ |
|
pNode->Clear(); |
|
m_FreeList.InsertHead(pNode); |
|
} |
|
} |
|
else |
|
{ |
|
pNode->Clear(); |
|
m_FreeList.InsertHead(pNode); |
|
} |
|
} |
|
if (bNotify && m_cpNotifySink) |
|
{ |
|
hr = m_cpNotifySink->Notify(); |
|
} |
|
} |
|
return hr; |
|
}; |
|
|
|
|
|
inline void CSpEventSource::_MoveAllToFreeList(CSpEventList * pList) |
|
{ |
|
CSpEventNode * pNode; |
|
while ((pNode = pList->RemoveHead()) != NULL) |
|
{ |
|
pNode->Clear(); |
|
m_FreeList.InsertHead(pNode); |
|
} |
|
} |
|
inline void CSpEventSource::_RemoveAllEvents( ) |
|
{ |
|
m_pParent->Lock(); |
|
|
|
_MoveAllToFreeList(&m_CompletedList); |
|
_MoveAllToFreeList(&m_PendingList); |
|
m_pParent->Unlock(); |
|
} |
|
|
|
inline HRESULT CSpEventSource::_GetStreamNumber(const ULONGLONG ullAudioOffset, ULONG *pulStreamNum) |
|
{ |
|
CSpEventNode *pNode = m_PendingList.m_pHead; |
|
*pulStreamNum = m_ulStreamNum; |
|
for(;pNode && pNode->ullAudioStreamOffset <= ullAudioOffset; pNode = pNode->m_pNext) |
|
{ |
|
*pulStreamNum = pNode->ulStreamNum; |
|
} |
|
return S_OK; |
|
} |
|
|
|
|
|
|
|
#endif //--- This must be the last line in this file
|
|
|