hxaudstr_new.cpp
上传用户:dangjiwu
上传日期:2013-07-19
资源大小:42019k
文件大小:89k
- /* ***** BEGIN LICENSE BLOCK *****
- * Source last modified: $Id: hxaudstr_new.cpp,v 1.21.2.1.2.2 2005/07/08 18:12:41 gwright Exp $
- *
- * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
- *
- * The contents of this file, and the files included with this file,
- * are subject to the current version of the RealNetworks Public
- * Source License (the "RPSL") available at
- * http://www.helixcommunity.org/content/rpsl unless you have licensed
- * the file under the current version of the RealNetworks Community
- * Source License (the "RCSL") available at
- * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
- * will apply. You may also obtain the license terms directly from
- * RealNetworks. You may not use this file except in compliance with
- * the RPSL or, if you have a valid RCSL with RealNetworks applicable
- * to this file, the RCSL. Please see the applicable RPSL or RCSL for
- * the rights, obligations and limitations governing use of the
- * contents of the file.
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License Version 2 or later (the
- * "GPL") in which case the provisions of the GPL are applicable
- * instead of those above. If you wish to allow use of your version of
- * this file only under the terms of the GPL, and not to allow others
- * to use your version of this file under the terms of either the RPSL
- * or RCSL, indicate your decision by deleting the provisions above
- * and replace them with the notice and other provisions required by
- * the GPL. If you do not delete the provisions above, a recipient may
- * use your version of this file under the terms of any one of the
- * RPSL, the RCSL or the GPL.
- *
- * This file is part of the Helix DNA Technology. RealNetworks is the
- * developer of the Original Code and owns the copyrights in the
- * portions it created.
- *
- * This file, and the files included with this file, is distributed
- * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
- * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
- * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
- * ENJOYMENT OR NON-INFRINGEMENT.
- *
- * Technology Compatibility Kit Test Suite(s) Location:
- * http://www.helixcommunity.org/content/tck
- *
- * Contributor(s):
- *
- * ***** END LICENSE BLOCK ***** */
- #include "hlxclib/stdio.h"
- #include "hlxclib/string.h"
- #include "hxresult.h"
- #include "hxtypes.h"
- #include "hxcom.h"
- #include "hxengin.h"
- #include "ihxpckts.h"
- #include "hxbuffer.h"
- #include "hxausvc.h"
- #include "hxrasyn.h"
- #include "hxprefs.h"
- #include "hxerror.h"
- #include "errdbg.h"
- #include "chxpckts.h"
- #include "hxaudply.h"
- #include "hxaudstr.h"
- #include "hxaudses.h"
- #include "hxaudvol.h"
- #include "mixengine.h"
- #include "hxslist.h"
- #include "hxmap.h"
- #include "auderrs.h"
- #include "hxtick.h"
- #include "hxheap.h"
- #ifdef _DEBUG
- #undef HX_THIS_FILE
- static const char HX_THIS_FILE[] = __FILE__;
- #endif
- #define CACHE_INCREMENT_SIZE 2
- //#define _TESTING 1
- #ifdef _TESTING
- #include <fcntl.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #if defined (_WINDOWS) || defined (_WIN32)
- #include <io.h>
- #endif
- int g_log = -1;
- #endif
- /************************************************************************
- * Method:
- * IHXAudioStream::CHXAudioStream()
- * Purpose:
- * Constructor.
- */
- CHXAudioStream::CHXAudioStream(CHXAudioPlayer* owner, IUnknown* pContext)
- : m_lRefCount(0)
- , m_wLastError(HXR_OK)
- , m_pResampler(NULL)
- , m_pValues(0)
- , m_bDisableWrite(FALSE)
- , m_ulGranularity(0)
- , m_ulInputBytesPerGran(0)
- , m_ulOutputBytesPerGran(0)
- , m_pDataList(0)
- , m_pInstantaneousList(0)
- , m_pRAByToTsInList(0)
- , m_pRAByToTsAdjustedList(0)
- , m_bFirstWrite(TRUE)
- , m_bHooksInitialized(FALSE)
- , m_bInited(FALSE)
- , m_bSetupDone(FALSE)
- , m_bAudioFormatKnown(FALSE)
- , m_ulMaxBlockSize(0)
- , m_uVolume(HX_MAX_VOLUME)
- , m_bMute(FALSE)
- , m_bGotHooks(FALSE)
- , m_llLastWriteTime(0)
- , m_ulFudge(5)
- , m_pInDataPtr(0)
- , m_pOutDataPtr(0)
- , m_bTobeTimed(TRUE)
- , m_bIsFirstPacket(TRUE)
- , m_bIsLive(FALSE)
- , m_ulBaseTime(0)
- , m_ulLiveDelay(0)
- , m_bSetupToBeDone(FALSE)
- , m_bCrossFadingToBeDone(FALSE)
- , m_pCrossFadeStream(NULL)
- , m_llCrossFadeStartTime(0)
- , m_ulCrossFadeDuration(0)
- , m_bFadeToThisStream(FALSE)
- , m_bFadeAlreadyDone(FALSE)
- , m_bRealAudioStream(FALSE)
- , m_ulLastInputStartTime(0)
- , m_ulLastInputEndTime(0)
- , m_llLastStartTimePlayed(0)
- , m_ulTSRollOver(0)
- , m_bLastWriteTimeUpdated(FALSE)
- , m_pCommonClassFactory(NULL)
- , m_pAvailableBuffers(NULL)
- , m_uCacheSize(CACHE_INCREMENT_SIZE)
- , m_bCacheMayBeGrown(FALSE)
- , m_bDeterminedInitialCacheSize(FALSE)
- , m_bLastNMilliSecsToBeSaved(FALSE)
- , m_ulLastNMilliSeconds(MINIMUM_INITIAL_PUSHDOWN)
- , m_pLastNMilliSecsList(NULL)
- , m_ulLastNHeadTime(0)
- , m_ulLastNTailTime(0)
- , m_eState(E_STOPPED)
- , m_bCanBeRewound(FALSE)
- , m_bAudioDeviceReflushHint(FALSE)
- , m_bIsResumed(FALSE)
- , m_bPlayerPause(FALSE)
- , m_pPreferences(NULL)
- , m_bMayNeedToRollbackTimestamp(FALSE)
- , m_piPendingAudioData(NULL)
- {
- m_Owner = owner;
- if (m_Owner)
- {
- m_Owner->AddRef();
- }
- if (pContext)
- {
- HX_VERIFY(HXR_OK == pContext->QueryInterface(IID_IHXCommonClassFactory,
- (void**) &m_pCommonClassFactory));
- }
- #ifdef HELIX_FEATURE_VOLUME
- m_pStreamVolume = NULL;
- #endif
-
- #if defined(HELIX_FEATURE_PREFERENCES)
- if (pContext)
- {
- HX_VERIFY(HXR_OK == pContext->QueryInterface(IID_IHXPreferences, (void**) &m_pPreferences));
- }
- #endif /* HELIX_FEATURE_PREFERENCES */
- m_DryNotificationMap = new CHXMapPtrToPtr;
- m_pInDataPtr = new HXAudioData;
- m_pOutDataPtr = new HXAudioData;
- m_pMixEngine = new HXAudioSvcMixEngine() ;
- };
- /************************************************************************
- * Method:
- * IHXAudioStream::~CHXAudioStream()
- * Purpose:
- * Destructor. Clean up and set free.
- */
- CHXAudioStream::~CHXAudioStream()
- {
- HX_DELETE(m_DryNotificationMap);
- ResetStream();
- HX_RELEASE(m_piPendingAudioData);
- }
-
- /////////////////////////////////////////////////////////////////////////
- // Method:
- // IUnknown::QueryInterface
- // Purpose:
- // Implement this to export the interfaces supported by your
- // object.
- //
- STDMETHODIMP CHXAudioStream::QueryInterface(REFIID riid, void** ppvObj)
- {
- QInterfaceList qiList[] =
- {
- { GET_IIDHANDLE(IID_IHXAudioStream), (IHXAudioStream*)this },
- { GET_IIDHANDLE(IID_IHXRealAudioSync), (IHXRealAudioSync*)this },
- { GET_IIDHANDLE(IID_IHXAudioStream2), (IHXAudioStream2*)this },
- { GET_IIDHANDLE(IID_IHXCommonClassFactory), (IHXCommonClassFactory*)this },
- { GET_IIDHANDLE(IID_IHXUpdateProperties), (IHXUpdateProperties*)this },
- { GET_IIDHANDLE(IID_IUnknown), (IUnknown*)(IHXAudioStream*)this },
- #ifdef HELIX_FEATURE_VOLUME
- { GET_IIDHANDLE(IID_IHXVolumeAdviseSink), (IHXVolumeAdviseSink*)this },
- #endif
- };
-
- return ::QIFind(qiList, QILISTSIZE(qiList), riid, ppvObj);
- }
- /////////////////////////////////////////////////////////////////////////
- // Method:
- // IUnknown::AddRef
- // Purpose:
- // Everyone usually implements this the same... feel free to use
- // this implementation.
- //
- STDMETHODIMP_(ULONG32) CHXAudioStream::AddRef()
- {
- return InterlockedIncrement(&m_lRefCount);
- }
- /////////////////////////////////////////////////////////////////////////
- // Method:
- // IUnknown::Release
- // Purpose:
- // Everyone usually implements this the same... feel free to use
- // this implementation.
- //
- STDMETHODIMP_(ULONG32) CHXAudioStream::Release()
- {
- if (InterlockedDecrement(&m_lRefCount) > 0)
- {
- return m_lRefCount;
- }
- delete this;
- return 0;
- }
- /*
- * IHXAudioStream methods
- */
- /************************************************************************
- * Method:
- * IHXAudioStream::Init
- * Purpose:
- * Init the audio stream.
- */
- STDMETHODIMP CHXAudioStream::Init
- (
- const HXAudioFormat* pAudioFormat,
- IHXValues* pValues
- )
- {
- if (m_bAudioFormatKnown)
- {
- return HXR_OK;
- }
- HX_RESULT theErr = HXR_OK;
- m_pValues = pValues;
- if (m_pValues)
- {
- m_pValues->AddRef();
- UINT32 ulVal = 0;
- m_pValues->GetPropertyULONG32("audioDeviceReflushHint", ulVal);
- if (ulVal == 1)
- {
- SetAudioDeviceReflushHint(TRUE);
- m_Owner->m_Owner->CheckIfLastNMilliSecsToBeStored();
- }
- }
- memcpy( &m_AudioFmt, pAudioFormat, sizeof(HXAudioFormat) );
- // Create the audio data list
- m_pDataList = new CHXSimpleList;
- if ( !m_pDataList )
- theErr = HXR_OUTOFMEMORY;
- if(!theErr) // check if list constructor really succeeded
- {
- if(!m_pDataList->IsPtrListValid())
- theErr = HXR_OUTOFMEMORY;
- }
- m_pInstantaneousList = new CHXSimpleList;
- if ( !m_pInstantaneousList || !m_pInstantaneousList->IsPtrListValid())
- theErr = HXR_OUTOFMEMORY;
- // Reset this so that we init the hooks
- m_bFirstWrite = TRUE;
- m_bHooksInitialized = FALSE;
- #ifdef HELIX_FEATURE_VOLUME
- if( !theErr )
- {
- m_pStreamVolume = (IHXVolume*)new CHXVolume;
- if( m_pStreamVolume )
- {
- m_pStreamVolume->AddRef();
- m_pStreamVolume->AddAdviseSink(this);
- }
- else
- theErr = HXR_OUTOFMEMORY;
- }
- #endif
- m_bAudioFormatKnown = TRUE;
- if (m_bSetupToBeDone)
- {
- m_bSetupToBeDone = FALSE;
- m_Owner->AudioFormatNowKnown();
- }
- if (!theErr && m_bSetupDone && !m_bInited)
- {
- theErr = ProcessInfo();
- }
- return theErr;
- }
- /************************************************************************
- * Method:
- * IHXAudioStream::Write
- * Purpose:
- * Write audio data to Audio Services.
- *
- * NOTE: If the renderer loses packets and there is no loss
- * correction, then the renderer should write the next packet
- * using a meaningful start time. Audio Services will play
- * silence where packets are missing.
- */
- STDMETHODIMP CHXAudioStream::Write
- (
- HXAudioData* pInData
- )
- {
- HX_RESULT theErr = HXR_OK;
- if (!pInData)
- {
- return HXR_INVALID_PARAMETER;
- }
- if (!m_bInited)
- {
- return HXR_NOT_INITIALIZED;
- }
- // Init pre-mix hooks. Call this once to set up hook info.
- if ( !m_bHooksInitialized )
- {
- InitHooks();
- }
- #if defined(HELIX_FEATURE_AUDIO_INCOMPLETESAMPLE)
- /* make sure that we are always handing complete sample frames
- * down the chain by buffering up samples if we don't get complete frames.
- *
- * This is done by using a slush IHXBuffer that is just large enough
- * to hold one sample frame, and a second IHXBuffer to hold sample fragments.
- * This one does not own a buffer, but uses the incoming buffers.
- *
- * This way, we don't need any large memcpy()s. Unfortunately, we have to
- * create a new slush buffer every time because we don't know how long
- * this buffer will be stuck in the queue until it gets rendered.
- *
- * I guess we could try to be smart and only create a new one if the old one
- * is still AddRef()ed but I figure it's not worth it.
- */
- /* if there was a discontinuity in the audio stream, throw away pending bytes.
- * This also assumes that we're starting at a sample frame boundary, which might
- * be wrong -- but we really have no way to tell.
- */
- if (pInData->uAudioStreamType != STREAMING_AUDIO)
- {
- m_ulPendingAudioBytes = 0 ;
- HX_RELEASE(m_piPendingAudioData) ;
- }
- /* if pData is NULL, hand it through unchanged. See comment in Write2() */
- if (!pInData->pData)
- return Write2(pInData) ;
- UINT32 ulInBytes = pInData->pData->GetSize() ; // number of bytes in incoming sample
- UINT32 ulCutoffBytes = 0 ; // number of bytes that will be cut off incoming sample
- /* first check if we have pending samples. */
- if (m_ulPendingAudioBytes)
- {
- // complete sample frames would have been sent the last time around
- HX_ASSERT(m_ulPendingAudioBytes < m_ulSampleFrameSize) ;
- HX_ASSERT(m_piPendingAudioData) ;
- /* append more bytes from the start of the incoming packet. */
- ulCutoffBytes = m_ulSampleFrameSize - m_ulPendingAudioBytes ;
- if (ulCutoffBytes > ulInBytes)
- ulCutoffBytes = ulInBytes ;
- memcpy(m_piPendingAudioData->GetBuffer() + m_ulPendingAudioBytes,
- pInData->pData->GetBuffer(),
- ulCutoffBytes ) ;
- m_ulPendingAudioBytes += ulCutoffBytes ;
- ulInBytes -= ulCutoffBytes ;
- }
- // if we have a complete sample frame in the slush buffer, send it.
- if (m_ulPendingAudioBytes == m_ulSampleFrameSize)
- {
- HX_ASSERT(m_piPendingAudioData) ;
- HXAudioData audioData ;
- audioData.pData = m_piPendingAudioData ;
- audioData.pData->AddRef() ;
- // use time of incoming packet -- one sample frame is below the
- // ms resolution of time stamps.
- audioData.ulAudioTime = pInData->ulAudioTime;
- // stream type is incoming stream type
- audioData.uAudioStreamType = pInData->uAudioStreamType;
- theErr = Write2(&audioData) ;
- audioData.pData->Release() ;
- m_ulPendingAudioBytes = 0 ;
- // release the slush buffer
- HX_RELEASE(m_piPendingAudioData) ;
- if (FAILED(theErr))
- return theErr ;
- }
- // put partial sample frames from the end of the incoming buffer
- // into the slush buffer.
- if (ulInBytes % m_ulSampleFrameSize)
- {
- // the slush buffer should be empty here.
- HX_ASSERT(m_ulPendingAudioBytes == 0);
- HX_ASSERT(m_piPendingAudioData == 0) ;
- // reserve a new slush buffer
- theErr = CreateInstance(IID_IHXBuffer, (void**)&m_piPendingAudioData);
- if (SUCCEEDED(theErr))
- theErr = m_piPendingAudioData->SetSize(m_ulSampleFrameSize) ;
- if (SUCCEEDED(theErr))
- {
- m_ulPendingAudioBytes = ulInBytes % m_ulSampleFrameSize ;
- ulInBytes -= m_ulPendingAudioBytes ;
- memcpy(m_piPendingAudioData->GetBuffer(),
- pInData->pData->GetBuffer() + ulCutoffBytes + ulInBytes,
- m_ulPendingAudioBytes) ;
- }
- if (FAILED(theErr))
- return theErr ;
- }
- // send any leftover fragment of the incoming buffer.
- if (ulInBytes == pInData->pData->GetSize() && !ulCutoffBytes)
- /* this is the entire buffer, not a fragment.
- * This is the normal case -- let's handle it efficiently. */
- {
- theErr = Write2(pInData) ;
- }
- else if (ulInBytes)
- /* if anything left in buffer, send it in a fragment */
- {
- HXAudioData audioData ;
- CHXBufferFragment* pFragment = new CHXBufferFragment(
- pInData->pData,
- pInData->pData->GetBuffer() + ulCutoffBytes,
- ulInBytes);
- theErr = pFragment->QueryInterface(IID_IUnknown, (void**)&audioData.pData) ;
- // this must always succeed, since we know it exports a IHXBuffer
- HX_ASSERT(SUCCEEDED(theErr)) ;
- // use time of incoming packet -- one sample frame is below the
- // ms resolution of time stamps.
- audioData.ulAudioTime = pInData->ulAudioTime;
- // stream type is incoming stream type if we did not cut anything away,
- // and STREAMED_AUDIO if we did (because in that case this is a continuation)
- audioData.uAudioStreamType = ulCutoffBytes ? STREAMING_AUDIO : pInData->uAudioStreamType ;
- theErr = Write2(&audioData) ;
- // we release our hold on pFragment here. When MixIntoBuffer() is done with
- // this fragment, it will also release its hold, and the fragment gets
- // deleted.
- audioData.pData->Release() ;
- }
- #else
- theErr = Write2(pInData) ;
- #endif /* HELIX_FEATURE_AUDIO_INCOMPLETESAMPLE */
- return theErr;
- }
- /************************************************************************
- * Method:
- * IHXAudioStream::Write2
- * Purpose:
- * Write audio data to Audio Services. This is a companion/backend
- * function to IHXAudioStream::Write
- *
- */
- HX_RESULT CHXAudioStream::Write2(HXAudioData* pInData)
- {
- HX_RESULT theErr = HXR_OK;
- // Process any "hooks"; Add the data to the data list.
- /* If buffer is NULL, it means that the user just
- * wants to know what timestamp should be placed in the next
- * STREAMED/TIMED audio data
- */
- if ( !m_bGotHooks || !pInData->pData)
- {
- theErr = AddData( pInData );
- }
- else
- {
- HXAudioData outData;
-
- outData.pData = 0;
- outData.ulAudioTime = 0;
- theErr = ProcessHooks( pInData, &outData );
- if (!theErr && !m_bDisableWrite )
- {
- theErr = AddData( &outData );
- }
- if (outData.pData)
- {
- outData.pData->Release();
- }
- }
-
- return theErr;
- }
- /************************************************************************
- * Method:
- * IHXAudioStream::AddPreMixHook
- * Purpose:
- * Use this method to add a pre-mix audio data hook.
- */
- STDMETHODIMP CHXAudioStream::AddPreMixHook
- (
- IHXAudioHook* pHook,
- const BOOL bDisableWrite
- )
- {
- #if defined(HELIX_FEATURE_AUDIO_PREMIXHOOK)
- void* pTmp = 0;
-
- /* Does one already exists */
- if (m_PreMixHookMap.Lookup((void*)pHook, pTmp))
- {
- return HXR_INVALID_PARAMETER;
- }
- HXAudioHookInfo* pPreMixHookInfo = (HXAudioHookInfo*) new HXAudioHookInfo;
- pPreMixHookInfo->pHook = pHook;
- pPreMixHookInfo->bDisableWrite = bDisableWrite;
- pPreMixHookInfo->bFinal = FALSE;
- pPreMixHookInfo->bIgnoreAudioData = FALSE;
- pPreMixHookInfo->bMultiChannelSupport = FALSE;
- IHXValues* pValues = NULL;
- if (pHook && pHook->QueryInterface(IID_IHXValues, (void**) &pValues) == HXR_OK)
- {
- UINT32 ulValue = 0;
- pValues->GetPropertyULONG32("IgnoreAudioData", ulValue);
- pPreMixHookInfo->bIgnoreAudioData = (ulValue == 1);
- HX_RELEASE(pValues);
- }
- pHook->AddRef(); // Released in destructor
- IHXAudioMultiChannel* pMultiChannel = NULL;
- if (pHook && HXR_OK == pHook->QueryInterface(IID_IHXAudioMultiChannel, (void**) &pMultiChannel))
- {
- pPreMixHookInfo->bMultiChannelSupport = pMultiChannel->GetMultiChannelSupport();
- }
- HX_RELEASE(pMultiChannel);
- m_PreMixHookMap.SetAt(pHook, pPreMixHookInfo);
- m_bGotHooks = TRUE;
- /* If any one of them is Disabled, we do not write */
- if (bDisableWrite)
- {
- m_bDisableWrite = TRUE;
- }
- ProcessAudioHook(ACTION_ADD, pHook);
- /* If we are already initialized, send the audio format */
- if (m_bHooksInitialized)
- {
- if (pPreMixHookInfo->bIgnoreAudioData ||
- HXR_OK == ProcessAudioHook(ACTION_CHECK, pHook))
- {
- pHook->OnInit( &m_AudioFmt );
- }
- }
- return HXR_OK;
- #else
- return HXR_NOTIMPL;
- #endif /* HELIX_FEATURE_AUDIO_PREMIXHOOK */
- }
- /************************************************************************
- * Method:
- * IHXAudioStream::RemovePreMixHook
- * Purpose:
- * Use this method to remove a pre-mix audio data hook.
- */
- STDMETHODIMP CHXAudioStream::RemovePreMixHook
- (
- IHXAudioHook* pHook
- )
- {
- #if defined(HELIX_FEATURE_AUDIO_PREMIXHOOK)
- HXAudioHookInfo* pPreMixHookInfo = 0;
- BOOL bCheckForDisableWrite = FALSE;
- if (!m_PreMixHookMap.Lookup((void*)pHook, (void*&) pPreMixHookInfo))
- {
- return HXR_INVALID_PARAMETER;
- }
- m_PreMixHookMap.RemoveKey(pHook);
- /* If we are removing a hook which had disable write,
- * we need to re-determine if any of the remaining hooks
- * has DisableWrite set to TRUE
- */
- if (pPreMixHookInfo->bDisableWrite)
- {
- bCheckForDisableWrite = TRUE;
- m_bDisableWrite = FALSE;
- }
- ProcessAudioHook(ACTION_REMOVE, pHook);
- pPreMixHookInfo->pHook->Release();
- delete pPreMixHookInfo;
- if (m_PreMixHookMap.GetCount() == 0)
- {
- m_bGotHooks = FALSE;
- m_bDisableWrite = FALSE;
- }
- else if (bCheckForDisableWrite)
- {
- CHXMapPtrToPtr::Iterator lIter = m_PreMixHookMap.Begin();
- for (; lIter != m_PreMixHookMap.End(); ++lIter)
- {
- HXAudioHookInfo* pPreMixHook = (HXAudioHookInfo*) (*lIter);
-
- /* atleast one has Disable Write ON */
- if (pPreMixHook->bDisableWrite)
- {
- m_bDisableWrite = TRUE;
- break;
- }
- }
- }
- #endif /* HELIX_FEATURE_AUDIO_PREMIXHOOK */
- return HXR_OK;
- }
- /************************************************************************
- * Method:
- * IHXAudioStream::AddDryNotification
- * Purpose:
- * Use this to add a notification response object to get notifications
- * when audio stream is running dry.
- */
- STDMETHODIMP CHXAudioStream::AddDryNotification
- (
- IHXDryNotification* /*IN*/ pNotification
- )
- {
- if (!pNotification)
- {
- return HXR_INVALID_PARAMETER;
- }
- void* pTmp = 0;
-
- /* Does one already exists */
- if (m_DryNotificationMap->Lookup((void*)pNotification, pTmp))
- {
- return HXR_INVALID_PARAMETER;
- }
- pNotification->AddRef();
- m_DryNotificationMap->SetAt((void*)pNotification, (void*)pNotification);
- return HXR_OK;
- }
- /************************************************************************
- * Method:
- * IHXAudioStream2::RemoveDryNotification
- * Purpose:
- * Use this to remove itself from the notification response object
- * during the stream switching.
- */
- STDMETHODIMP CHXAudioStream::RemoveDryNotification
- (
- IHXDryNotification* /*IN*/ pNotification
- )
- {
- HX_RESULT hr = HXR_OK;
- void* pTmp = 0;
- if (!pNotification)
- {
- hr = HXR_INVALID_PARAMETER;
- goto cleanup;
- }
- // remove only if it is exists
- if (m_DryNotificationMap->Lookup((void*)pNotification, pTmp))
- {
- m_DryNotificationMap->RemoveKey((void*)pNotification);
- HX_RELEASE(pNotification);
- }
- else
- {
- hr = HXR_INVALID_PARAMETER;
- goto cleanup;
- }
- cleanup:
- return hr;
- }
- /************************************************************************
- * Method:
- * IHXAudioStream2::GetAudioFormat
- * Purpose:
- * Returns the input audio format of the data written by the
- * renderer. This function will fill in the pre-allocated
- * HXAudioFormat structure passed in.
- */
- STDMETHODIMP
- CHXAudioStream::GetAudioFormat(HXAudioFormat* /*IN/OUT*/pAudioFormat)
- {
- HX_ASSERT(pAudioFormat);
- if (!pAudioFormat)
- {
- return HXR_INVALID_PARAMETER;
- }
- if (!m_bInited)
- {
- return HXR_UNEXPECTED;
- }
- pAudioFormat->uChannels = m_AudioFmt.uChannels;
- pAudioFormat->uBitsPerSample = m_AudioFmt.uBitsPerSample;
- pAudioFormat->ulSamplesPerSec = m_AudioFmt.ulSamplesPerSec;
- pAudioFormat->uMaxBlockSize = m_AudioFmt.uMaxBlockSize;
- return HXR_OK;
- }
-
- /************************************************************************
- * Method:
- * IHXAudioStream::GetAudioVolume
- * Purpose:
- * Return this stream's IRMA volume interface.
- */
- STDMETHODIMP_(IHXVolume*) CHXAudioStream::GetAudioVolume()
- {
- IHXVolume* pRet = NULL;
-
- #ifdef HELIX_FEATURE_VOLUME
- if( m_pStreamVolume )
- {
- m_pStreamVolume->AddRef();
- pRet = m_pStreamVolume;
- }
- #endif
-
- return pRet;
- }
- #if defined(HELIX_FEATURE_VOLUME)
- //
- // IHXVolume methods
- //
- STDMETHODIMP CHXAudioStream::OnVolumeChange(const UINT16 uVolume)
- {
- m_uVolume = uVolume;
- #ifdef HELIX_FEATURE_GAINTOOL
- if (m_pMixEngine)
- m_pMixEngine->SetVolume(m_pMixEngine->HXVolume2TenthOfDB(m_uVolume)) ;
- #endif
- return HXR_OK;
- }
- STDMETHODIMP CHXAudioStream::OnMuteChange(const BOOL bMute)
- {
- m_bMute = bMute;
- #ifdef HELIX_FEATURE_GAINTOOL
- if (m_pMixEngine)
- m_pMixEngine->SetVolume(m_pMixEngine->HXVolume2TenthOfDB(bMute ? HX_MIN_VOLUME : m_uVolume)) ;
- #endif
- return HXR_OK;
- }
- #endif /* HELIX_FEATURE_VOLUME */
- /************************************************************************
- * Method:
- * IHXAudioStream::AddData
- * Purpose:
- * Add audio data to list.
- * NOTE: Mark Streamed data also as Timed data IF we don't write a streamed packet
- * since it was LATE!!!
- */
- HX_RESULT CHXAudioStream::AddData
- (
- HXAudioData* pAudioData
- )
- {
- HX_RESULT theErr = HXR_OK;
- BOOL bInTSRollOver = FALSE;
- HXAudioInfo* pAinfo = 0;
- /* If buffer is NULL, it means that the user just
- * wants to know what timestamp should be placed in the next
- * STREAMED/TIMED audio data
- */
- if (!pAudioData->pData)
- {
- HXAudioInfo* pInfo = NULL;
- if (!m_pDataList->IsEmpty() &&
- NULL != (pInfo = (HXAudioInfo*) m_pDataList->GetTail()))
- {
- pAudioData->ulAudioTime = pInfo->ulStartTime +
- CalcMs(pInfo->pBuffer->GetSize());
- }
- else
- {
- pAudioData->ulAudioTime = INT64_TO_UINT32(m_llLastWriteTime - CAST_TO_INT64 (m_ulTSRollOver) * CAST_TO_INT64 (MAX_UINT32));
- }
- return HXR_OK;
- }
- // make sure the renderer does not pass NULL data!!
- HX_ASSERT(pAudioData->pData->GetBuffer() != NULL &&
- pAudioData->pData->GetSize() != 0);
- if (pAudioData->pData->GetBuffer() == NULL ||
- pAudioData->pData->GetSize() == 0)
- {
- return HXR_INVALID_PARAMETER;
- }
- if (m_bIsFirstPacket)
- {
- m_bIsFirstPacket = FALSE;
-
- IHXErrorMessages* pErrMsg = NULL;
- if (HXR_OK == m_Owner->m_pContext->QueryInterface(IID_IHXErrorMessages, (void**)&pErrMsg))
- {
- DEBUG_OUT(pErrMsg, DOL_GENERIC, (s,"AudioFormatIn: %lu channels %lu SamplesPerSec", m_AudioFmt.uChannels, m_AudioFmt.ulSamplesPerSec));
- DEBUG_OUT(pErrMsg, DOL_GENERIC, (s,"AudioFormatOut: %lu channels %lu SamplesPerSec", m_DeviceFmt.uChannels, m_DeviceFmt.ulSamplesPerSec));
- }
- HX_RELEASE(pErrMsg);
- if (m_bIsLive)
- {
- // XXX wschildbach why do we update a last write time *here*?
- m_Owner->UpdateStreamLastWriteTime();
- UpdateStreamLastWriteTime(TRUE);
- }
- }
- //{FILE* f1 = ::fopen("c:\temp\audio.txt", "a+"); ::fprintf(f1, "%lutAddDatat%pt%lun", HX_GET_BETTERTICKCOUNT(), this, pAudioData->ulAudioTime);::fclose(f1);}
- // ::fwrite(pAudioData->pData->GetBuffer(), pAudioData->pData->GetSize(), 1, fdin);
- UINT32 ulDataTime = CalcMs(pAudioData->pData->GetSize());
- UINT32 ulEndTime = pAudioData->ulAudioTime + ulDataTime;
- if (m_pAvailableBuffers && !m_bDeterminedInitialCacheSize && ulDataTime > 0)
- {
- m_bDeterminedInitialCacheSize = TRUE;
- m_uCacheSize = (UINT16) (m_ulGranularity*2/ulDataTime) + 1;
- /* make sure it is atleast CACHE_INCREMENT_SIZE to begin with */
- m_uCacheSize = m_uCacheSize < CACHE_INCREMENT_SIZE ?
- CACHE_INCREMENT_SIZE : m_uCacheSize;
- }
- if (m_ulLastInputStartTime > pAudioData->ulAudioTime &&
- ((m_ulLastInputStartTime - pAudioData->ulAudioTime) > MAX_TIMESTAMP_GAP))
- {
- bInTSRollOver = TRUE;
- m_ulTSRollOver++;
- }
- m_ulLastInputStartTime = pAudioData->ulAudioTime;
- m_ulLastInputEndTime = ulEndTime;
- /* even in STREAMING_AUDIO case, it might happen, that the packets
- * written are late. e.g. packets received late on the network
- */
- // XXX wschildbach : Why do we even care? Just insert the packet into the queue
- // and let the reaper take care of it!
- INT64 llActualTimestamp = CAST_TO_INT64 (pAudioData->ulAudioTime) + CAST_TO_INT64 m_ulTSRollOver * CAST_TO_INT64 MAX_UINT32;
- INT64 llActualEndTime = CAST_TO_INT64 (pAudioData->ulAudioTime) + CAST_TO_INT64 (ulDataTime) +
- CAST_TO_INT64 (m_ulTSRollOver) * CAST_TO_INT64 (MAX_UINT32);
- #if 0 // testing sampling frequency estimation for "inaccurate resampling"
- if (pAudioData->uAudioStreamType == TIMED_AUDIO)
- {
- m_startMeasureTime = llActualTimestamp ;
- m_totSamples = 0 ;
- }
- else if (pAudioData->uAudioStreamType == STREAMING_AUDIO)
- {
- float diffTime = (llActualTimestamp - m_startMeasureTime) / 1000.0f ;
- if (diffTime)
- {
- float frEstimate = m_totSamples / m_AudioFmt.uChannels / diffTime ;
- float frEstErr = frEstimate * 0.001f / diffTime ; // 1 ms inaccuracy
- {FILE *f1 = fopen("c:\temp\estimatesr.txt","a+");
- fprintf(f1,"%I64dt%I64dt%ft%fn",llActualTimestamp,llActualEndTime,frEstimate,frEstErr);
- fclose(f1);
- }
- }
- }
- m_totSamples += CalcSamples(pAudioData->pData->GetSize()) ;
- #endif
- if ((pAudioData->uAudioStreamType == STREAMING_AUDIO ||
- pAudioData->uAudioStreamType == TIMED_AUDIO) &&
- !(llActualTimestamp >= m_llLastWriteTime ||
- llActualEndTime > m_llLastWriteTime))
- {
- /* Too late*/
- m_bTobeTimed = TRUE;
- //{FILE* f1 = ::fopen("e:\audio.txt", "a+"); ::fprintf(f1, "%lut%pt%dt%lut%lutLATEn", HX_GET_BETTERTICKCOUNT(), this, m_pDataList->GetCount(), pAudioData->ulAudioTime, (INT32)m_llLastWriteTime);::fclose(f1);}
- /*
- {FILE *f1 = fopen("c:\temp\mix.txt","a+"); fprintf(f1,"LATE packetn");
- fclose(f1);
- }
- */
-
- return HXR_LATE_PACKET;
- }
- pAinfo = new HXAudioInfo;
- if( !pAinfo )
- {
- return HXR_OUTOFMEMORY;
- }
- pAudioData->pData->AddRef();
- pAinfo->pBuffer = pAudioData->pData;
- pAinfo->ulStartTime = pAudioData->ulAudioTime;
- pAinfo->pOffset = pAudioData->pData->GetBuffer();
- pAinfo->ulBytesLeft = pAudioData->pData->GetSize();
- pAinfo->uAudioStreamType = pAudioData->uAudioStreamType;
- if (m_bTobeTimed && pAudioData->uAudioStreamType == STREAMING_AUDIO)
- {
- pAinfo->uAudioStreamType = TIMED_AUDIO;
- m_bTobeTimed = FALSE;
- }
- else if (m_bTobeTimed && pAudioData->uAudioStreamType == TIMED_AUDIO)
- {
- m_bTobeTimed = FALSE;
- }
- //{FILE* f1 = ::fopen("c:\temp\audio.txt", "a+"); ::fprintf(f1, "AddData ulAudioTime: %lun", pAudioData->ulAudioTime);::fclose(f1);}
- //////////////////////////////////////////////////////////////////////
- // XXX wschildbach
- // the start time of this packet in samples. This may be corrected later on.
- pAinfo->llStartTimeInSamples = CAST_TO_INT64(llActualTimestamp) * m_AudioFmt.ulSamplesPerSec / 1000 * m_AudioFmt.uChannels ;
- pAinfo->llEndTimeInSamples = pAinfo->llStartTimeInSamples + Bytes2Samples(pAinfo->pBuffer->GetSize(), &m_AudioFmt) ;
- //////////////////////////////////////////////////////////////////////
- if (pAinfo->uAudioStreamType == INSTANTANEOUS_AUDIO)
- {
- CHXSimpleList* pList = new CHXSimpleList;
- if( !pList )
- {
- HX_RELEASE(pAudioData->pData);
- HX_DELETE(pAinfo);
- return HXR_OUTOFMEMORY;
- }
- pList->AddHead((void*) pAinfo);
- m_pInstantaneousList->AddTail((void*) pList);
- m_Owner->m_Owner->ToBeRewound();
- }
- else if (pAinfo->uAudioStreamType == STREAMING_INSTANTANEOUS_AUDIO)
- {
- HX_ASSERT(m_pInstantaneousList && m_pInstantaneousList->GetCount() > 0);
- CHXSimpleList* pList = NULL;
- if (m_pInstantaneousList->GetCount() == 0)
- {
- pList = new CHXSimpleList;
- if( !pList )
- {
- HX_RELEASE(pAudioData->pData);
- HX_DELETE(pAinfo);
- return HXR_OUTOFMEMORY;
- }
- m_pInstantaneousList->AddTail((void*) pList);
- // fix for naive users!
- pAinfo->uAudioStreamType = INSTANTANEOUS_AUDIO;
- m_Owner->m_Owner->ToBeRewound();
- }
- pList = (CHXSimpleList*) m_pInstantaneousList->GetTail();
- pList->AddTail(pAinfo);
- }
- else if (m_pDataList->IsEmpty())
- {
- /*
- {FILE *f1 = fopen("c:\temp\mix.txt","a+"); fprintf(f1,"adding to empty list...n");
- fclose(f1);
- }
- */
- m_pDataList->AddTail((void*) pAinfo);
- }
- else
- {
- /*
- {FILE *f1 = fopen("c:\temp\mix.txt","a+"); fprintf(f1,"adding to non-empty list...n");
- fclose(f1);
- }
- */
- HXAudioInfo* pInfo = (HXAudioInfo*) m_pDataList->GetTail();
- UINT32 ulActualTSRollOver = m_ulTSRollOver;
- if (bInTSRollOver && ulActualTSRollOver)
- {
- ulActualTSRollOver--;
- }
- INT64 llActualLastEndTime = CAST_TO_INT64 (pInfo->ulStartTime) + CAST_TO_INT64 (CalcMs(pInfo->pBuffer->GetSize())) +
- CAST_TO_INT64 ulActualTSRollOver * CAST_TO_INT64 MAX_UINT32;
- INT64 llActualLastStartTime = CAST_TO_INT64 (pInfo->ulStartTime) + CAST_TO_INT64 ulActualTSRollOver * CAST_TO_INT64 MAX_UINT32;
-
- if (llActualTimestamp < llActualLastStartTime)
- {
- /* Not allowed */
- theErr = HXR_OUTOFORDER_PACKET;
- /* something is wrong... figure out what?*/
- HX_ASSERT(!("Packets written out of order"));
- goto exit;
- }
- if (pAinfo->uAudioStreamType == STREAMING_AUDIO)
- {
- /* is it a resonable packet to add to the list */
- if ((llActualTimestamp <= llActualLastEndTime &&
- llActualLastEndTime - llActualTimestamp <= m_ulFudge) ||
- (llActualTimestamp >= llActualLastEndTime &&
- llActualTimestamp - llActualLastEndTime <= m_ulFudge))
- {
- // XXX wschildbach
- // make 64-bit timestamps contiguous before adding into the queue.
- pAinfo->llEndTimeInSamples += pInfo->llEndTimeInSamples - pAinfo->llStartTimeInSamples ;
- pAinfo->llStartTimeInSamples = pInfo->llEndTimeInSamples ;
- m_pDataList->AddTail((void*) pAinfo);
-
- }
- else
- {
- theErr = HXR_NONCONTIGUOUS_PACKET; //HX_LATE_PACKET;
- /* something is wrong... figure out what?*/
- HX_ASSERT(!("Streaming Audio: Non-Contigous Write"));
- m_bTobeTimed = TRUE;
- goto exit;
- }
- }
- else
- {
- /* see if there is any overlap.. we do not allow any overlap */
- if (llActualTimestamp < llActualLastEndTime &&
- llActualLastEndTime - llActualTimestamp > m_ulFudge)
- {
- /* hmmm an overlapped packet */
- theErr = HXR_OVERLAPPED_PACKET;
- /* something is wrong... figure out what?*/
- HX_ASSERT(!("Timed Audio: Overlapping write"));
- m_bTobeTimed = TRUE;
- goto exit;
- }
- else
- {
- m_pDataList->AddTail((void*) pAinfo);
- }
- }
- }
- exit:
- if (theErr != HXR_OK && pAinfo)
- {
- pAinfo->pBuffer->Release();
- delete pAinfo;
- }
- //{FILE* f1 = ::fopen("e:\audio.txt", "a+"); ::fprintf(f1, "%lut%pt%dt%lut%lun", HX_GET_BETTERTICKCOUNT(), this, m_pDataList->GetCount(), pAudioData->ulAudioTime, (UINT32)m_llLastWriteTime);::fclose(f1);}
- return theErr;
- }
- HX_RESULT CHXAudioStream::ProcessInfo(void)
- {
- HX_RESULT theErr = HXR_OK;
- m_ulSampleFrameSize = m_AudioFmt.uChannels * (m_AudioFmt.uBitsPerSample>>3) ;
- m_ulPendingAudioBytes = 0 ;
- // Calculate the number of bytes per granularity.
- // XXX wschildbach: These formulas are suspect. There is no feedback to the player
- // or audio session that this is what we assume for a size. I believe this is either
- // not needed or too complex.
- m_ulInputBytesPerGran = (ULONG32)
- (((m_AudioFmt.uChannels * (m_AudioFmt.uBitsPerSample>>3) * m_AudioFmt.ulSamplesPerSec)
- / 1000.0) * m_ulGranularity);
- m_ulOutputBytesPerGran = (ULONG32)
- (((m_DeviceFmt.uChannels * (m_DeviceFmt.uBitsPerSample>>3) * m_DeviceFmt.ulSamplesPerSec)
- / 1000.0) * m_ulGranularity);
- // Make sure that number of bytes per granularity is an even number.
- m_ulInputBytesPerGran -= m_ulInputBytesPerGran % ((m_AudioFmt.uBitsPerSample>>3)*m_AudioFmt.uChannels);
- m_ulOutputBytesPerGran -= m_ulOutputBytesPerGran % ((m_DeviceFmt.uBitsPerSample>>3)*m_DeviceFmt.uChannels);
- if (!theErr)
- {
- // set up the mixing engine
- // XXX wschildbach
- theErr = m_pMixEngine->Init(m_AudioFmt.ulSamplesPerSec, m_DeviceFmt.ulSamplesPerSec, m_AudioFmt.uChannels, m_DeviceFmt.uChannels) ;
- if (SUCCEEDED(theErr))
- theErr = m_pMixEngine->SetSampleConverter(this) ;
- if (SUCCEEDED(theErr))
- theErr = m_pMixEngine->SetOutputBytesPerSample(m_DeviceFmt.uBitsPerSample / 8) ;
- // set the volume (somebody might have set it when we did not have an engine)
- #ifdef HELIX_FEATURE_GAINTOOL
- if (SUCCEEDED(theErr))
- m_pMixEngine->SetVolume(m_pMixEngine->HXVolume2TenthOfDB(m_bMute ? HX_MIN_VOLUME : m_uVolume)) ;
- #endif
- }
- if (!theErr)
- {
- m_bInited = TRUE;
-
- if (m_eState == E_STOPPED)
- {
- m_eState = E_INITIALIZED;
- }
- }
- /* Get the current player time to set the last write audio time
- * If someone creates a stream mid presentation, we ask the player
- * object for the current write time.
- */
- // set last write time to be the current playback time since
- // this is what other system components(i.e. renderers) based on
- // fixed b#69847 - loss of push-down-worth of data =
- // m_Owner->GetLastAudioWriteTime() - m_Owner->GetCurrentPlayBackTime()
- // m_llLastWriteTime = m_Owner->GetCurrentPlayBackTime();
- // XXXRA: It is necessary to use last audio write time for any delayed
- // audio streams to work that do not involve any Pause/Rewind logic.
- // To cover the case where a source (and an audio stream) has been added
- // mid-playback by SMIL renderer which has a delay equivalent to the
- // current playback time, it should result in a player rewind which should
- // reset the lastaudiowrite time accordingly...so we should be able
- // to use m_Owner->GetLastAudioWriteTime() value in such a use case as well.
- // this change is required to fix PR 79161 and PR 69780.
- // Henry, PR 69847 (the reason for the earlier change) is still busted.
- // so I am reverting this code to the original code. you will have
- // to come up with a different fix for PR 69847 since this was clearly not
- // the correct fix.
- m_llLastWriteTime = m_Owner->GetLastAudioWriteTime();
- m_pMixEngine->ResetTimeLineInMillis(m_llLastWriteTime) ;
- if (!theErr && m_bInited)
- {
- m_Owner->StreamInitialized(this);
- }
- return theErr;
- }
- /************************************************************************
- * Method:
- * IHXAudioStream::GetFormat
- * Purpose:
- * Return the stream's audio format.
- */
- HX_RESULT CHXAudioStream::GetFormat
- (
- HXAudioFormat* pAudioFormat
- )
- {
- if (!m_bAudioFormatKnown)
- {
- return HXR_NOT_INITIALIZED;
- }
- pAudioFormat->uChannels = m_AudioFmt.uChannels;
- pAudioFormat->uBitsPerSample = m_AudioFmt.uBitsPerSample;
- pAudioFormat->ulSamplesPerSec = m_AudioFmt.ulSamplesPerSec;
- pAudioFormat->uMaxBlockSize = m_AudioFmt.uMaxBlockSize;
- return HXR_OK;
- }
- /************************************************************************
- * Method:
- * IHXAudioStream::Setup
- * Purpose:
- * This is called by the player's Setup method. At this
- * time the audio device format is set and we can now
- * set up the streams pre-mixing buffer. This buffer
- * contains data that has been resampled to match the
- * audio device format.
- */
- HX_RESULT CHXAudioStream::Setup(
- HXAudioFormat* pFormat
- , ULONG32 ulGranularity
- )
- {
- HX_RESULT theErr = HXR_OK;
- memcpy( &m_DeviceFmt, pFormat, sizeof(HXAudioFormat) );
- m_ulGranularity = ulGranularity;
- m_bSetupDone = TRUE;
-
- /* we have all the info now.. so setup the resampler */
- if (m_bAudioFormatKnown && !m_bInited)
- {
- theErr = ProcessInfo();
- }
- return theErr;
- }
- /************************************************************************
- * Method:
- * IHXAudioStream::ResetStream
- * Purpose:
- */
- void CHXAudioStream::ResetStream()
- {
- m_bInited = FALSE;
- m_bCanBeRewound = FALSE;
- m_bSetupDone = FALSE;
- m_bAudioFormatKnown = FALSE;
- m_bIsResumed = FALSE;
-
- UnRegister();
- while (m_pAvailableBuffers && m_pAvailableBuffers->GetCount() > 0)
- {
- IHXBuffer* pBuffer = (IHXBuffer*) m_pAvailableBuffers->RemoveHead();
- HX_RELEASE(pBuffer);
- }
- HX_DELETE(m_pAvailableBuffers);
- // Delete all entries in the audio data list
- FlushBuffers();
- HX_DELETE(m_pDataList);
- HX_DELETE(m_pInstantaneousList);
- CleanupRAByToTs();
- HX_DELETE(m_pRAByToTsInList);
- HX_DELETE(m_pRAByToTsAdjustedList);
- HX_DELETE(m_pMixEngine);
- m_bGotHooks = FALSE;
- m_llLastWriteTime = 0;
- m_ulTSRollOver = 0;
- HX_RELEASE(m_pValues);
- #if defined(HELIX_FEATURE_AUDIO_PREMIXHOOK)
- // Delete all entries in the pre-mix hook list.
- if ( m_PreMixHookMap.GetCount() > 0)
- {
- HXAudioHookInfo* h = 0;
- CHXMapPtrToPtr::Iterator lIter = m_PreMixHookMap.Begin();
- for (; lIter != m_PreMixHookMap.End(); ++lIter)
- {
- h = (HXAudioHookInfo*) (*lIter);
- ProcessAudioHook(ACTION_REMOVE, h->pHook);
- h->pHook->Release();
- delete h;
- }
-
- m_PreMixHookMap.RemoveAll();
- }
- #endif /* HELIX_FEATURE_AUDIO_PREMIXHOOK */
- #ifdef HELIX_FEATURE_VOLUME
- if( m_pStreamVolume )
- {
- m_pStreamVolume->RemoveAdviseSink(this);
- m_pStreamVolume->Release();
- m_pStreamVolume=NULL;
- }
- #endif
-
- HX_DELETE(m_pInDataPtr);
- HX_DELETE(m_pOutDataPtr);
- if (m_DryNotificationMap && m_DryNotificationMap->GetCount() > 0)
- {
- IHXDryNotification* pDryNotification = 0;
- CHXMapPtrToPtr::Iterator lIter = m_DryNotificationMap->Begin();
- for (; lIter != m_DryNotificationMap->End(); ++lIter)
- {
- pDryNotification = (IHXDryNotification*) (*lIter);
- pDryNotification->Release();
- }
-
- m_DryNotificationMap->RemoveAll();
- }
- HX_RELEASE(m_pCrossFadeStream);
- HX_RELEASE(m_pCommonClassFactory);
- #if defined(HELIX_FEATURE_PREFERENCES)
- HX_RELEASE(m_pPreferences);
- #endif /* HELIX_FEATURE_PREFERENCES */
- HX_RELEASE(m_Owner);
- return;
- }
- HX_RESULT
- CHXAudioStream::ProcessAudioHook(PROCESS_ACTION action,
- IHXAudioHook* pAudioHook)
- {
- return HXR_OK;
- }
- /************************************************************************
- * Method:
- * IHXAudioStream::InitHooks
- * Purpose:
- * Init any pre-mix hooks. Return TRUE if hooks exist else return
- * FALSE.
- */
- void CHXAudioStream::InitHooks()
- {
- #if defined(HELIX_FEATURE_AUDIO_PREMIXHOOK)
- /* Iterate thru the hook list and call the hook's OnInit().
- * If any of the hooks have disabled write set to TRUE, then
- * we will let this override any set to FALSE.
- */
- if ( m_PreMixHookMap.GetCount() > 0 )
- {
- HXAudioHookInfo* h = 0;
- CHXMapPtrToPtr::Iterator lIter = m_PreMixHookMap.Begin();
- for (; lIter != m_PreMixHookMap.End(); ++lIter)
- {
- h = (HXAudioHookInfo*) (*lIter);
- if (h->bIgnoreAudioData ||
- HXR_OK == ProcessAudioHook(ACTION_CHECK, h->pHook))
- {
- h->pHook->OnInit( &m_AudioFmt );
- }
- }
- }
- #endif /* HELIX_FEATURE_AUDIO_PREMIXHOOK */
- m_bHooksInitialized = TRUE;
- }
- /************************************************************************
- * Method:
- * IHXAudioStream::ProcessHooks
- * Purpose:
- */
- HX_RESULT CHXAudioStream::ProcessHooks
- (
- HXAudioData* pInData,
- HXAudioData* pOutData
- )
- {
- HX_RESULT theErr = HXR_OK;
- #if defined(HELIX_FEATURE_AUDIO_PREMIXHOOK)
- m_pInDataPtr->pData = pInData->pData;
- m_pInDataPtr->pData->AddRef();
- m_pInDataPtr->ulAudioTime = pInData->ulAudioTime;
-
- m_pOutDataPtr->pData = NULL;
- m_pOutDataPtr->ulAudioTime = pInData->ulAudioTime;
- m_pInDataPtr->uAudioStreamType = pInData->uAudioStreamType;
- m_pOutDataPtr->uAudioStreamType = pInData->uAudioStreamType;
- if ( m_PreMixHookMap.GetCount() > 0 )
- {
- HXAudioHookInfo* pPreMixHookInfo = 0;
- CHXMapPtrToPtr::Iterator lIter = m_PreMixHookMap.Begin();
- for (; !theErr && lIter != m_PreMixHookMap.End(); ++lIter)
- {
- pPreMixHookInfo = (HXAudioHookInfo*) (*lIter);
- if (HXR_OK == ProcessAudioHook(ACTION_CHECK, pPreMixHookInfo->pHook))
- {
- // XXXHP, disable hooks when it doesn't support multi-channel
- if (m_AudioFmt.uChannels <= 2 || pPreMixHookInfo->bMultiChannelSupport)
- {
- theErr = pPreMixHookInfo->pHook->OnBuffer( m_pInDataPtr, m_pOutDataPtr);
- /* Check to see if renderer changed the buffer. If so, then
- * make this output as input to the next Hook.
- */
- if (!theErr && m_pOutDataPtr->pData)
- {
- m_pInDataPtr->pData->Release();
- m_pInDataPtr->pData = m_pOutDataPtr->pData;
- m_pInDataPtr->ulAudioTime = m_pOutDataPtr->ulAudioTime;
- m_pOutDataPtr->pData = 0;
- }
- }
- }
- else if (pPreMixHookInfo->bIgnoreAudioData)
- {
- IHXBuffer* pTempBuf = m_pInDataPtr->pData;
- m_pInDataPtr->pData = NULL;
- theErr = pPreMixHookInfo->pHook->OnBuffer( m_pInDataPtr, m_pOutDataPtr);
- m_pInDataPtr->pData = pTempBuf;
- }
- }
- }
- /* Final output is always in InDataPtr*/
- pOutData->pData = m_pInDataPtr->pData;
- pOutData->ulAudioTime = m_pInDataPtr->ulAudioTime;
- pOutData->uAudioStreamType = m_pInDataPtr->uAudioStreamType;
- #endif /* HELIX_FEATURE_AUDIO_PREMIXHOOK */
- return theErr;
- }
- /************************************************************************
- * Method:
- * CHXAudioStream::MixIntoBuffer
- * Purpose:
- * Mix stream data into this pPlayerBuf.
- *
- */
- HX_RESULT CHXAudioStream::MixIntoBuffer
- (
- UCHAR* pPlayerBuf,
- ULONG32 ulBufSize,
- ULONG32& ulBufTime,
- BOOL& bIsMixBufferDirty,
- BOOL bGetCrossFadeData
- )
- {
- HX_RESULT res = HXR_OK ;
- if (!m_bInited)
- {
- return HXR_NOT_INITIALIZED;
- }
- // bGetCrossFadeData should now be a thing of the past.
- HX_ASSERT(!bGetCrossFadeData) ;
- //{FILE* f1 = ::fopen("c:\temp\rasync.txt", "a+"); ::fprintf(f1, "Call MixIntoBuffer: %lun", m_ulLastWriteTime);::fclose(f1);}
- /* If this is a *FROM* stream, we may have already mixed
- * data during cross-fade with *TO* stream
- */
- // update the outside world's sense of time.
- // XXX wschildbach: how to account for rollover?
- INT64 llNextMixTime = m_pMixEngine->GetNextMixTimeMillis();
- UINT32 ulLastWriteTime = INT64_TO_UINT32(llNextMixTime - CAST_TO_INT64(m_ulTSRollOver) * CAST_TO_INT64(MAX_UINT32));
- // In a surestream situation, ulBufTime is 0 on the first stream, and a time corresponding
- // to the granularity in the second stream. I don't know what this code tries to do.
- // XXX wschildbach
- if (ulBufTime < ulLastWriteTime)
- {
- ulBufTime = ulLastWriteTime;
- }
- /* If there are any DryNotifications and the data list is empty
- * we need to notify them so that they can write more data.
- *
- * If EnoughDataAvailable() returns FALSE, it will have updated
- * the input values to point at where the buffer needs to be filled.
- */
- INT64 llStartMix, llEndMix;
- m_pMixEngine->GetMixRange(ulBufSize, llStartMix, llEndMix) ;
- UINT32 nSamplesNeeded = INT64_TO_UINT32(llEndMix - llStartMix) ; // always fits into UINT32
- if (!EnoughDataAvailable(llStartMix, nSamplesNeeded))
- {
- // Check if the audio device is really empty ("ReallyNeedData") or if we are
- // just over-eagerly filling up the pre-roll. If the latter is the case,
- // return HXR_WOULD_BLOCK
- if (!bIsMixBufferDirty && !m_Owner->m_Owner->ReallyNeedData())
- {
- return HXR_WOULD_BLOCK;
- }
- // renderer might pause playback if has no packets
- if (m_DryNotificationMap->GetCount() > 0)
- {
- IHXDryNotification* pDryNotification = 0;
- CHXMapPtrToPtr::Iterator lIter = m_DryNotificationMap->Begin();
- for (; lIter != m_DryNotificationMap->End(); ++lIter)
- {
- pDryNotification = (IHXDryNotification*) (*lIter);
- UINT64 streamTime = Samples2Ms(llStartMix, &m_AudioFmt) -
- CAST_TO_INT64(m_ulTSRollOver)*CAST_TO_INT64(MAX_UINT32);
- HX_RESULT theErr = pDryNotification->OnDryNotification( INT64_TO_UINT32(streamTime),
- INT64_TO_UINT32(Samples2Ms(nSamplesNeeded, &m_AudioFmt))) ;
- if( theErr == HXR_OUTOFMEMORY )
- {
- return theErr;
- }
- }
- if (m_Owner->GetState() != E_PLAYING)
- {
- return HXR_OK;
- }
- }
- }
- // XXX wschildbach what does this do?
- m_Owner->DataInAudioDevice(TRUE);
- /*
- {FILE *f1 = fopen("c:\temp\mix.txt","a+");
- fprintf(f1,"ncall MixIntoBuffer(%I64d, id=%ld)n",(INT64)mixTimeInSamples,m_pMixEngine - (HXAudioSvcMixEngine*)0xdde198);
- fclose(f1);}
- */
- // this call does all the mixing.
- res = m_pMixEngine->MixIntoBuffer(pPlayerBuf, ulBufSize, bIsMixBufferDirty) ;
- if( m_wLastError == HXR_OUTOFMEMORY )
- {
- return m_wLastError;
- }
-
- if (FAILED(res))
- return res ;
- #if defined(HELIX_FEATURE_AUDIO_INACCURATESAMPLING)
- if( m_bRealAudioStream )
- MapFudgedTimestamps();
- #endif
-
- // update the inside world's sense of time.
- m_llLastWriteTime = m_pMixEngine->GetNextMixTimeMillis() ;
- #if 0
- {FILE *f = fopen("c:\temp\incoming.txt","a+");
- fprintf(f,"mix %ld %I64dn",ulBufTime,m_llLastWriteTime);fclose(f);}
- #endif
- return HXR_OK;
- }
- /*
- * This is the callback function that m_pMixEngine->MixIntoBuffer() will call to read
- * new samples.
- */
- BOOL CHXAudioStream::ConvertIntoBuffer(tAudioSample* buffer, UINT32 nSamples, INT64 llStartTimeInSamples)
- {
- HXAudioInfo* pInfo = 0;
- LISTPOSITION lp = 0;
- INT32 nBytesPerSample = m_AudioFmt.uBitsPerSample>>3 ;
- BOOL didMix = FALSE ; // set to TRUE if we have packets in this time range
- BOOL bPacketsAfterRange = FALSE;
- /*
- {FILE *f1 = fopen("c:\temp\mix.txt","a+");
- fprintf(f1," ConvertIntoBuffer(%I64d - %I64d, len=%ld)n",(INT64)llStartTimeInSamples,(INT64)llStartTimeInSamples+nSamples,nSamples);
- fclose(f1);}
- */
- // there are two lists of packets here: timed audio and instantaneous audio.
- // We only look into the list for timed buffers -- Instantaneoue audio is ignored
- // (it never properly worked anyway, so support is discontinued).
- /* remove old packets. Old packets are packets that have an end time that is before
- our current mix time. */
- lp = m_pDataList->GetHeadPosition();
- while( lp )
- {
- LISTPOSITION lastlp = lp;
- pInfo = (HXAudioInfo*) m_pDataList->GetNext(lp);
- if (pInfo->llEndTimeInSamples < llStartTimeInSamples)
- {
- /*
- {FILE *f1 = fopen("c:\temp\mix.txt","a+");
- fprintf(f1,"-- reaping packet (%I64d,%I64d)n",
- pInfo->llStartTimeInSamples,pInfo->llEndTimeInSamples);
- fclose(f1);}
- */
- FreeInfo(pInfo);
- m_pDataList->RemoveAt(lastlp);
- if( m_wLastError == HXR_OUTOFMEMORY )
- {
- return FALSE;
- }
- }
- else // if monotonous and non-overlapping
- break ;
- }
- // now go through the entire list of packets, and look for overlap with the
- // convert buffer. Any packet with overlap will be at least partially converted
- // into the buffer.
- // If packets overlap, one packet will then take precedence over another -- not
- // much we can do about that.
- lp = m_pDataList->GetHeadPosition();
- while( lp )
- {
- pInfo = (HXAudioInfo*) m_pDataList->GetNext(lp);
- if (pInfo->llStartTimeInSamples < llStartTimeInSamples + nSamples &&
- pInfo->llEndTimeInSamples > llStartTimeInSamples)
- {
- // This packet has some overlap with what we are converting.
- // if this is the first packet to be mixed into this buffer,
- // silence out the entire buffer. This is inefficient, but safe
- if (!didMix)
- CAudioSvcSampleConverter::silence(buffer, nSamples) ;
- didMix = TRUE ;
- INT32 nMixbufferOffset = 0;
- INT32 pastPacketStart = INT64_TO_INT32(llStartTimeInSamples - pInfo->llStartTimeInSamples) ;
- if (pastPacketStart < 0)
- {
- nMixbufferOffset = -pastPacketStart;
- pastPacketStart = 0 ;
- }
- INT32 nn = Bytes2Samples(pInfo->pBuffer->GetSize(), &m_AudioFmt) ;
- INT32 nSamplesToUse = nn - pastPacketStart;
- if (nSamplesToUse > (INT32)(nSamples - nMixbufferOffset))
- nSamplesToUse = nSamples - nMixbufferOffset;
- const unsigned char *cvtin = pInfo->pBuffer->GetBuffer() + pastPacketStart * nBytesPerSample ;
- tAudioSample *cvtout = buffer + nMixbufferOffset ;
- /*
- {FILE *f1 = fopen("c:\temp\mix.txt","a+");
- fprintf(f1," mix packet (%I64d,%I64d,len=%ld) into buffer(%ld,%ld,len=%ld)n",
- pInfo->llStartTimeInSamples,pInfo->llEndTimeInSamples,
- nn,
- nMixbufferOffset,nMixbufferOffset+nSamplesToUse,nSamplesToUse);
- fprintf(f1," pastPacketStart = %I64d, nMixbufferOffset = %ldn",
- pastPacketStart,nMixbufferOffset) ;
- fclose(f1);}
- */
- switch (nBytesPerSample)
- {
- case 1:
- CAudioSvcSampleConverter::cvt8(cvtin, cvtout, nSamplesToUse);
- break ;
- case 2:
- CAudioSvcSampleConverter::cvt16(cvtin, cvtout, nSamplesToUse);
- break ;
- case 4:
- CAudioSvcSampleConverter::cvt32(cvtin, cvtout, nSamplesToUse);
- break ;
- }
- }
- else if (pInfo->llStartTimeInSamples >= llStartTimeInSamples + nSamples)
- {
- /* We've found audio data that is past the
- * desired range.
- */
- bPacketsAfterRange = TRUE;
- }
- }
- if (!didMix && bPacketsAfterRange)
- {
- /* We do not have packets for this range, but we
- * do have packets after the range.
- * Create silence data and make it look like we
- * actually had data for this range.
- */
- CAudioSvcSampleConverter::silence(buffer, nSamples) ;
- didMix = TRUE;
- }
- return didMix ;
- }
- /************************************************************************
- * Method:
- * CHXAudioStream::Bytes2Samples
- * Purpose:
- * Translate from units of bytes to samples.
- */
- UINT32 CHXAudioStream::Bytes2Samples
- (
- UINT64 ulNumBytes,
- const HXAudioFormat *fmt
- )
- {
- ASSERT(ulNumBytes % (fmt->uBitsPerSample >> 3) == 0) ;
- return INT64_TO_UINT32(ulNumBytes / (fmt->uBitsPerSample >> 3)) ;
- }
- /************************************************************************
- * Method:
- * CHXAudioStream::Samples2Ms
- * Purpose:
- * Calculate the duration in millisecs for this number of samples.
- */
- UINT64 CHXAudioStream::Samples2Ms
- (
- INT64 nSamples,
- const HXAudioFormat *fmt
- )
- {
- UINT32 ulDenom = fmt->uChannels * fmt->ulSamplesPerSec;
- UINT64 q = nSamples / ulDenom;
- UINT64 r = nSamples - q * ulDenom;
- return q * 1000 + (r * 1000) / ulDenom;
- }
- /************************************************************************
- * Method:
- * CHXAudioStream::CalcMs
- * Purpose:
- * Calculate the duration in millisecs for this number of
- * bytes in input format.
- */
- ULONG32 CHXAudioStream::CalcMs
- (
- ULONG32 ulNumBytes
- )
- {
- return INT64_TO_ULONG32(Samples2Ms(Bytes2Samples(ulNumBytes, &m_AudioFmt), &m_AudioFmt));
- }
- /************************************************************************
- * Method:
- * CHXAudioStream::CalcDeviceMs
- * Purpose:
- * Calculate the duration in millisecs for this number of
- * bytes in Device format.
- */
- ULONG32 CHXAudioStream::CalcDeviceMs
- (
- ULONG32 ulNumBytes
- )
- {
- return INT64_TO_ULONG32(Samples2Ms(Bytes2Samples(ulNumBytes, &m_DeviceFmt), &m_DeviceFmt));
- }
- /************************************************************************
- * Method:
- * CHXAudioStream::CalcOffset
- * Purpose:
- * Calculate the offset in bytes given time.
- */
- UINT32 CHXAudioStream::CalcOffset
- (
- INT64 llStartTime
- , INT64 llEndTime
- )
- {
- /* Using m_ulBytesPerMs may introduce cumulative error due
- * to decimal cutoff
- */
- HX_ASSERT(llEndTime - llStartTime < MAX_TIMESTAMP_GAP);
- return m_ulGranularity ?
- INT64_TO_UINT32((llEndTime - llStartTime) * m_ulInputBytesPerGran / m_ulGranularity) :
- 0 ;
- }
- void CHXAudioStream::FlushBuffers(BOOL bInstantaneousAlso)
- {
- while (m_pDataList && m_pDataList->GetCount() > 0)
- {
- HXAudioInfo* pInfo = (HXAudioInfo*) m_pDataList->RemoveHead();
- FreeInfo(pInfo);
- }
- while (bInstantaneousAlso && m_pInstantaneousList && m_pInstantaneousList->GetCount() > 0)
- {
- CHXSimpleList* pList = (CHXSimpleList*) m_pInstantaneousList->RemoveHead();
- while (pList->GetCount() > 0)
- {
- HXAudioInfo* pInfo = (HXAudioInfo*) pList->RemoveHead();
- FreeInfo(pInfo, TRUE);
- }
- HX_DELETE(pList);
- }
- // reset m_bLastNMilliSecsToBeSaved so that we actually
- // delete buffers in FreeInfo
- BOOL bLastNMilliSecsToBeSaved = m_bLastNMilliSecsToBeSaved;
- m_bLastNMilliSecsToBeSaved = FALSE;
- while (m_pLastNMilliSecsList && m_pLastNMilliSecsList->GetCount() > 0)
- {
- HXAudioInfo* pInfo = (HXAudioInfo*) m_pLastNMilliSecsList->RemoveHead();
- FreeInfo(pInfo);
- }
- m_bLastNMilliSecsToBeSaved = bLastNMilliSecsToBeSaved;
- HX_DELETE(m_pLastNMilliSecsList);
- }
- /*
- this routine checks if there are enough packets waiting in the queue
- to be mixed. It will return FALSE if not, or if there are packets missing
- in the middle of the queue.
- */
- BOOL
- CHXAudioStream::EnoughDataAvailable(INT64& llStartTimeInSamples, UINT32& nSamplesRequired)
- {
- INT64 llEndTimeInSamples = llStartTimeInSamples + nSamplesRequired ;
- HXAudioInfo* pInfoOld = 0 ;
- HXAudioInfo* pInfo = 0;
- LISTPOSITION lp = 0;
- // if the list is completely empty, report the whole data range as missing
- if (m_pDataList->IsEmpty())
- return FALSE ;
- nSamplesRequired = 0 ;
- /* skip over old packets. Old packets are packets that have an end time that is before
- our current mix time. */
- lp = m_pDataList->GetHeadPosition();
- while( lp )
- {
- pInfoOld = (HXAudioInfo*) m_pDataList->GetNext(lp);
- if (pInfoOld->llEndTimeInSamples >= llStartTimeInSamples)
- break ;
- }
- #if 0 // disabled missing packet detection
- // pInfoOld is the first packet to be mixed. To make sure it overlaps with the start
- // of the mix buffer, do this (disabled for now):
-
- if (pInfoOld->llStartTimeInSamples > llStartTimeInSamples)
- return FALSE ;
- #endif
- // now go through the rest of packets, and make sure they are contiguous until
- // the end of our mix time
- // If packets overlap, one packet will then take precedence over another -- not
- // much we can do about that.
- while( lp )
- {
- pInfo = (HXAudioInfo*) m_pDataList->GetNext(lp);
- // if we see a packet with a timestamp after the mix time ("future packet")
- // or one that does not abut with the previous one ("discontinuity"), stop.
- if (pInfo->llStartTimeInSamples >= llEndTimeInSamples) // future packet
- // pInfo->llStartTimeInSamples != pInfoOld->llEndTimeInSamples) // discontinuity
- {
- break ;
- }
- pInfoOld = pInfo ;
- }
- // pInfoOld is the last packet to be mixed (or the last before a discontinuity).
- // Make sure it overlaps with the end of the mix buffer.
- if (pInfoOld->llEndTimeInSamples < llEndTimeInSamples)
- {
- llStartTimeInSamples = pInfoOld->llEndTimeInSamples ;
- nSamplesRequired = INT64_TO_UINT32(llEndTimeInSamples - llStartTimeInSamples) ;
- return FALSE ;
- }
- return TRUE ; // Data available!
- }
- HX_RESULT
- CHXAudioStream::StartCrossFade(CHXAudioStream* pFromStream,
- UINT32 ulCrossFadeStartTime,
- UINT32 ulCrossFadeDuration,
- BOOL bToStream)
- {
- #if defined(HELIX_FEATURE_CROSSFADE)
- // XXX wschildbach need to account for rollover.
- INT64 llStartTimeInSamples = CAST_TO_INT64(ulCrossFadeStartTime) * m_DeviceFmt.ulSamplesPerSec / 1000 * m_DeviceFmt.uChannels ;
- INT64 llEndTimeInSamples = (CAST_TO_INT64(ulCrossFadeStartTime)+ulCrossFadeDuration) * m_DeviceFmt.ulSamplesPerSec / 1000 * m_DeviceFmt.uChannels ;
- m_pMixEngine->SetCrossFade(bToStream ? HXAudioSvcMixEngine::FADE_IN : HXAudioSvcMixEngine::FADE_OUT,
- llStartTimeInSamples, llEndTimeInSamples) ;
- /*
- {
- FILE *f2 = fopen("c:\temp\mix.txt","a+");
- fprintf(f2,"** StartCrossFade(%I64d, %I64d, len=%ld, to=%sn",
- llStartTimeInSamples,
- llEndTimeInSamples,
- (INT32)(-llStartTimeInSamples+llEndTimeInSamples),
- bToStream?"yes":"no");
- fclose(f2);
- }
- */
- return HXR_OK;
- #else
- return HXR_NOTIMPL;
- #endif /* HELIX_FEATURE_CROSSFADE */
- }
- /*
- * IHXRealAudioSync methods
- */
- /************************************************************************
- * Method:
- * IHXRealAudioSync::Register
- * Purpose:
- */
- STDMETHODIMP
- CHXAudioStream::Register(void)
- {
- #if defined _DEBUG && defined HELIX_FEATURE_AUDIO_MULTIPLAYER_PAUSE
- if (HXDebugOptionEnabled("zDoNotUseFudge"))
- {
- return HXR_OK;
- }
- #endif
- if (m_bRealAudioStream)
- {
- return HXR_UNEXPECTED;
- }
- m_bRealAudioStream = TRUE;
- m_Owner->RegisterRealAudioStream(this);
- #if defined(HELIX_FEATURE_AUDIO_INACCURATESAMPLING)
- if (!m_pRAByToTsInList)
- {
- m_pRAByToTsInList = new CHXSimpleList;
- m_pRAByToTsAdjustedList = new CHXSimpleList;
- }
- #endif /* HELIX_FEATURE_AUDIO_INACCURATESAMPLING */
- return HXR_OK;
- }
- /************************************************************************
- * Method:
- * IHXRealAudioSync::UnRegister
- * Purpose:
- */
- STDMETHODIMP
- CHXAudioStream::UnRegister(void)
- {
- #if defined _DEBUG && defined HELIX_FEATURE_AUDIO_MULTIPLAYER_PAUSE
- if (HXDebugOptionEnabled("zDoNotUseFudge"))
- {
- return HXR_OK;
- }
- #endif
- if (!m_bRealAudioStream)
- {
- return HXR_UNEXPECTED;
- }
- m_bRealAudioStream = FALSE;
- m_Owner->UnRegisterRealAudioStream(this);
- CleanupRAByToTs();
- return HXR_OK;
- }
- /************************************************************************
- * Method:
- * IHXRealAudioSync::FudgeTimestamp
- * Purpose:
- * Tell the audio stream about the relationship between the number
- * of bytes written to the actual timestamp.
- *
- */
- STDMETHODIMP
- CHXAudioStream::FudgeTimestamp(UINT32 /*IN*/ ulNumberofBytes,
- UINT32 /*IN*/ ulTimestamp)
- {
- #if defined(HELIX_FEATURE_AUDIO_INACCURATESAMPLING)
- #if defined _DEBUG && defined HELIX_FEATURE_AUDIO_MULTIPLAYER_PAUSE
- if (HXDebugOptionEnabled("zDoNotUseFudge"))
- {
- return HXR_OK;
- }
- #endif
- RealAudioBytesToTimeStamp* pByToTs =
- new RealAudioBytesToTimeStamp;
- pByToTs->m_ulTimestamp = ulTimestamp;
- pByToTs->m_ulInTimestamp = m_ulLastInputStartTime;
- pByToTs->m_ulInEndTime = m_ulLastInputEndTime;
- if (m_bIsLive && m_ulBaseTime > 0)
- {
- pByToTs->m_ulTimestamp += m_ulLiveDelay;
- if (pByToTs->m_ulTimestamp > m_ulBaseTime)
- {
- pByToTs->m_ulTimestamp -= m_ulBaseTime;
- }
- else
- {
- pByToTs->m_ulTimestamp = 0;
- }
- }
- pByToTs->m_ulOrigTimestamp = pByToTs->m_ulTimestamp;
- m_pRAByToTsInList->AddTail((void*) pByToTs);
- #endif /* HELIX_FEATURE_AUDIO_INACCURATESAMPLING */
- //{FILE* f1 = ::fopen("d:\temp\audio.txt", "a+"); ::fprintf(f1, "Fudge:t%lut%lun", ulTimestamp, m_ulLastInputStartTime);::fclose(f1);}
- return HXR_OK;
- }
- void
- CHXAudioStream::CleanupRAByToTs(void)
- {
- #if defined(HELIX_FEATURE_AUDIO_INACCURATESAMPLING)
- if (!m_pRAByToTsInList)
- {
- return;
- }
- CHXSimpleList::Iterator ndx = m_pRAByToTsInList->Begin();
- for (; ndx != m_pRAByToTsInList->End(); ++ndx)
- {
- RealAudioBytesToTimeStamp* pByToTs =
- (RealAudioBytesToTimeStamp*) (*ndx);
- delete pByToTs;
- }
- m_pRAByToTsInList->RemoveAll();
- ndx = m_pRAByToTsAdjustedList->Begin();
- for (; ndx != m_pRAByToTsAdjustedList->End(); ++ndx)
- {
- RealAudioBytesToTimeStamp* pByToTs =
- (RealAudioBytesToTimeStamp*) (*ndx);
- delete pByToTs;
- }
- m_pRAByToTsAdjustedList->RemoveAll();
- #endif /* HELIX_FEATURE_AUDIO_INACCURATESAMPLING */
- }
- HX_RESULT
- CHXAudioStream::ConvertCurrentTime(double dBytesPlayed,
- UINT32 ulCurrentTime,
- UINT32& ulAdjustedTime)
- {
- #if defined(HELIX_FEATURE_AUDIO_INACCURATESAMPLING)
- HX_ASSERT(m_bRealAudioStream);
- ulAdjustedTime = ulCurrentTime;
- LISTPOSITION posRABytes = m_pRAByToTsAdjustedList->GetHeadPosition();
- RealAudioBytesToTimeStamp* pByToTsLower = NULL;
- RealAudioBytesToTimeStamp* pByToTsHigher = NULL;
- INT64 llActualByToTsHigherTimestamp = 0;
- INT64 llActualByToTsLowerTimestamp =0;
- while(posRABytes)
- {
- RealAudioBytesToTimeStamp* pByToTs = (RealAudioBytesToTimeStamp*)
- m_pRAByToTsAdjustedList->GetAt(posRABytes);
- if (dBytesPlayed >= pByToTs->m_ulOutNumBytes)
- {
- pByToTsLower = pByToTs;
- }
- else
- {
- if (pByToTsLower)
- {
- pByToTsHigher = pByToTs;
- }
- else
- {
- /* It means that this stream was added mid-presentation and we have not yet
- * played any bits from this stream. Maintain the current time and do not
- * fudge it.
- */
- return HXR_OK;
- }
- }
- if (pByToTsLower && pByToTsHigher)
- {
- break;
- }
- m_pRAByToTsAdjustedList->GetNext(posRABytes);
- }
- /* We got a range, interpolate */
- if (pByToTsLower && pByToTsHigher)
- {
- //{FILE* f1 = ::fopen("d:\temp\rasync.txt", "a+"); ::fprintf(f1, "ConvertLowHigh: dBytesPlayed: %f LowTS: %lu HighTS: %lu LowBytes: %f HighBytes: %fn", dBytesPlayed,pByToTsLower->m_ulTimestamp,pByToTsHigher->m_ulTimestamp, pByToTsLower->m_ulOutNumBytes,pByToTsHigher->m_ulOutNumBytes);::fclose(f1);}
- /* Need to re-visit this ASSERT. A check will do for now */
- #if 0
- HX_ASSERT((pByToTsHigher->m_ulTimestamp >=
- pByToTsLower->m_ulTimestamp) &&
- (pByToTsHigher->m_ulOutNumBytes >=
- pByToTsLower->m_ulOutNumBytes));
- #endif
- llActualByToTsHigherTimestamp = CAST_TO_INT64 (pByToTsHigher->m_ulTimestamp) + CAST_TO_INT64 m_ulTSRollOver * CAST_TO_INT64 MAX_UINT32;
- llActualByToTsLowerTimestamp = CAST_TO_INT64 (pByToTsLower->m_ulTimestamp) + CAST_TO_INT64 m_ulTSRollOver * CAST_TO_INT64 MAX_UINT32;
- if ((llActualByToTsHigherTimestamp >= llActualByToTsLowerTimestamp) &&
- (pByToTsHigher->m_ulOutNumBytes >= pByToTsLower->m_ulOutNumBytes))
- {
- ulAdjustedTime = pByToTsLower->m_ulTimestamp +
- (UINT32) (((dBytesPlayed - pByToTsLower->m_ulOutNumBytes)*1./
- (pByToTsHigher->m_ulOutNumBytes -
- pByToTsLower->m_ulOutNumBytes)) *
- INT64_TO_UINT32(llActualByToTsHigherTimestamp -
- llActualByToTsLowerTimestamp));
- //{FILE* f1 = ::fopen("d:\temp\multi.txt", "a+"); ::fprintf(f1, "ConvertLHINTER: %p %pt%lut%lut%lutt%lut%lut%lut%lun", this, m_Owner, (UINT32) dBytesPlayed, ulCurrentTime, ulAdjustedTime, pByToTsLower->m_ulTimestamp, (UINT32) pByToTsLower->m_ulOutNumBytes, pByToTsHigher->m_ulTimestamp, (UINT32) pByToTsHigher->m_ulOutNumBytes);::fclose(f1);}
- }
- else
- {
- ulAdjustedTime = pByToTsLower->m_ulTimestamp;
- //{FILE* f1 = ::fopen("d:\temp\multi.txt", "a+"); ::fprintf(f1, "ConvertLH: %p %pt%lut%lut%lun", this, m_Owner, (UINT32) dBytesPlayed, ulCurrentTime, ulAdjustedTime);::fclose(f1);}
- }
- //{FILE* f1 = ::fopen("d:\temp\rasync.txt", "a+"); ::fprintf(f1, "ConvertLowHigh: ulCurrentTime: %lu ulAdjustedTime: %lu dBytesPlayed: %f LowTS: %lu HighTS: %lun", ulCurrentTime, ulAdjustedTime, dBytesPlayed,pByToTsLower->m_ulTimestamp,pByToTsHigher->m_ulTimestamp);::fclose(f1);}
- }
- /* The best we can do is return the time of the nearest map */
- else if (pByToTsLower)
- {
- ulAdjustedTime = pByToTsLower->m_ulTimestamp;
- double dBytesDiff = dBytesPlayed - pByToTsLower->m_ulOutNumBytes;
- if (dBytesDiff > 0)
- {
- double dNumBytes = m_Owner->ConvertMsToBytes(pByToTsLower->m_ulDuration);
- if (dBytesDiff >= dNumBytes)
- {
- ulAdjustedTime += pByToTsLower->m_ulDuration;
- }
- else
- {
- ulAdjustedTime += (UINT32) (pByToTsLower->m_ulDuration * dBytesDiff *1./dNumBytes);
- }
- }
- //{FILE* f1 = ::fopen("d:\temp\rasync.txt", "a+"); ::fprintf(f1, "ConvertLower: ulCurrentTime: %lu ulAdjustedTime: %lu dBytesPlayed: %f LowTS: %lu m_ulOutNumBytes: %fn", ulCurrentTime, ulAdjustedTime, dBytesPlayed,pByToTsLower->m_ulTimestamp, pByToTsLower->m_ulOutNumBytes);::fclose(f1);}
- }
- /* Remove all maps before pByToTsLower */
- posRABytes = m_pRAByToTsAdjustedList->GetHeadPosition();
- while(posRABytes)
- {
- RealAudioBytesToTimeStamp* pByToTs =
- (RealAudioBytesToTimeStamp*) m_pRAByToTsAdjustedList->GetAt(posRABytes);
- if (pByToTs != pByToTsLower)
- {
- //{FILE* f1 = ::fopen("d:\temp\rasync.txt", "a+"); ::fprintf(f1, "Delete: OutBytes: %f OutTS: %lun", pByToTs->m_ulOutNumBytes, pByToTs->m_ulTimestamp);::fclose(f1);}
- delete pByToTs;
- posRABytes = m_pRAByToTsAdjustedList->RemoveAt(posRABytes);
- }
- else
- {
- break;
- }
- }
- #else
- ulAdjustedTime = ulCurrentTime;
- #endif /* HELIX_FEATURE_AUDIO_INACCURATESAMPLING */
- //{FILE* f1 = ::fopen("d:\temp\multi.txt", "a+"); ::fprintf(f1, "Convert: %p %pt%lut%lut%lun", this, m_Owner, (UINT32) dBytesPlayed, ulCurrentTime, ulAdjustedTime);::fclose(f1);}
- return HXR_OK;
- }
- void
- CHXAudioStream::MapFudgedTimestamps(void)
- {
- #if defined(HELIX_FEATURE_AUDIO_INACCURATESAMPLING)
- LISTPOSITION posRABytes = m_pRAByToTsInList->GetHeadPosition();
- INT64 llActualByToTsInEndTime = 0;
- INT64 llActualByToTsInStartTime = 0;
-
- while(posRABytes)
- {
- RealAudioBytesToTimeStamp* pByToTs =
- (RealAudioBytesToTimeStamp*) m_pRAByToTsInList->GetAt(posRABytes);
- llActualByToTsInStartTime = CAST_TO_INT64 (pByToTs->m_ulInTimestamp) + CAST_TO_INT64 m_ulTSRollOver * CAST_TO_INT64 MAX_UINT32;
- llActualByToTsInEndTime = CAST_TO_INT64 (pByToTs->m_ulInEndTime) + CAST_TO_INT64 m_ulTSRollOver * CAST_TO_INT64 MAX_UINT32;
- /* Too late */
- if (llActualByToTsInEndTime < m_llLastWriteTime)
- {
- posRABytes = m_pRAByToTsInList->RemoveAt(posRABytes);
- delete pByToTs;
- }
- else if (llActualByToTsInStartTime <= m_llLastWriteTime
- /*&& pByToTs->m_ulInEndTime >= m_ulLastStartTimePlayed*/)
- {
- /* These two values will be used in determining what time it is */
- // Number of bytes that have been written to the audio device till now*/
- pByToTs->m_ulOutNumBytes = m_Owner->NumberOfBytesWritten();
-
- HX_ASSERT(m_llLastWriteTime - llActualByToTsInStartTime < MAX_TIMESTAMP_GAP);
- /* Interpolate */
- UINT32 ulTimeDiff = INT64_TO_UINT32(m_llLastWriteTime - llActualByToTsInStartTime);
- pByToTs->m_ulTimestamp += ulTimeDiff;
-
- pByToTs->m_ulDuration = INT64_TO_UINT32((llActualByToTsInEndTime -
- llActualByToTsInStartTime) - CAST_TO_INT64 ulTimeDiff);
-
- posRABytes = m_pRAByToTsInList->RemoveAt(posRABytes);
- m_pRAByToTsAdjustedList->AddTail(pByToTs);
- //{FILE* f1 = ::fopen("d:\temp\multi.txt", "a+"); ::fprintf(f1, "Map: %p %pt%lut%lut%lut%lut%lutt%lun", this, m_Owner, (UINT32) pByToTs->m_ulOutNumBytes, pByToTs->m_ulInTimestamp, pByToTs->m_ulInEndTime, pByToTs->m_ulTimestamp, pByToTs->m_ulOrigTimestamp, m_llLastWriteTime);::fclose(f1);}
- }
- else
- {
- break;
- }
- }
- #endif /* HELIX_FEATURE_AUDIO_INACCURATESAMPLING */
- }
- // XXX wschildbach: How to implement this with the 64-bit timestamps?
- void
- CHXAudioStream::UpdateStreamLastWriteTime(BOOL bForceUpdate /*= FALSE*/)
- {
- if (m_bLastWriteTimeUpdated)
- {
- return;
- }
- m_bLastWriteTimeUpdated = TRUE;
- if (m_bIsLive)
- {
- if (!m_pValues || m_pValues->GetPropertyULONG32("LiveSyncStartTime", m_ulBaseTime) != HXR_OK)
- {
- if (bForceUpdate)
- {
- m_bIsLive = FALSE;
- m_ulBaseTime = 0;
- m_llLastWriteTime = m_Owner->GetLastAudioWriteTime();
- }
- else
- {
- /*
- * do not set it yet.. we will wait till the first
- * AddData call
- */
- m_bLastWriteTimeUpdated = FALSE;
- }
- }
- else
- {
- m_pValues->GetPropertyULONG32("Delay", m_ulLiveDelay);
- INT64 llLastPlayerWriteTime = m_Owner->GetLastAudioWriteTime();
- if (m_ulLiveDelay > 0 &&
- CAST_TO_INT64 m_ulLiveDelay > llLastPlayerWriteTime &&
- m_ulBaseTime > INT64_TO_UINT32(CAST_TO_INT64 m_ulLiveDelay-llLastPlayerWriteTime))
- {
- m_llLastWriteTime = CAST_TO_INT64 (m_ulBaseTime -
- INT64_TO_UINT32(CAST_TO_INT64 m_ulLiveDelay-llLastPlayerWriteTime));
- }
- else
- {
- m_llLastWriteTime = CAST_TO_INT64 m_ulBaseTime;
- }
- }
- }
- else
- {
- // XXX HP
- //
- // Prolbem:
- // when rewinding audio data upon resume, the audio-push-down worth of data
- // would be missing when the stream's first resume is at the middle of playback,
- //
- // Solution:
- // we need to adjust the m_llLastWriteTime to the last audio player write time
- if (m_Owner->IsResumed() && !m_bIsResumed)
- {
- m_llLastWriteTime = m_Owner->GetLastAudioWriteTime();
- }
- }
- if (m_bLastWriteTimeUpdated && m_pMixEngine)
- m_pMixEngine->ResetTimeLineInMillis(m_llLastWriteTime) ;
- }
- void
- CHXAudioStream::SaveLastNMilliSeconds(BOOL bSave, UINT32 ulNMilliSeconds)
- {
- m_bLastNMilliSecsToBeSaved = bSave; // TRUE; //
- m_ulLastNMilliSeconds = ulNMilliSeconds; // 2000;//
- HX_ASSERT(!m_bLastNMilliSecsToBeSaved || m_ulLastNMilliSeconds > 0);
- // ensure we need to save for atleast 1 sec
- if (m_bLastNMilliSecsToBeSaved && m_ulLastNMilliSeconds < 1000)
- {
- m_ulLastNMilliSeconds = 1000;
- }
- if (!m_bLastNMilliSecsToBeSaved)
- {
- while (m_pLastNMilliSecsList && m_pLastNMilliSecsList->GetCount() > 0)
- {
- HXAudioInfo* pInfo = (HXAudioInfo*) m_pLastNMilliSecsList->RemoveHead();
- FreeInfo(pInfo);
- }
- HX_DELETE(m_pLastNMilliSecsList);
- }
- }
- void
- CHXAudioStream::RewindStream(UINT32 ulTimeToRewind)
- {
- HX_ASSERT(m_bLastNMilliSecsToBeSaved);
- if (!m_bCanBeRewound)
- {
- return;
- }
- if (m_bLastNMilliSecsToBeSaved && m_pLastNMilliSecsList)
- {
- HX_ASSERT(m_llLastWriteTime >= ulTimeToRewind);
- if (m_llLastWriteTime >= ulTimeToRewind)
- {
- m_llLastWriteTime -= ulTimeToRewind;
- }
- else
- {
- m_llLastWriteTime = 0;
- }
- HXAudioInfo* pInfo = NULL;
- // reset any pInfo's in data list that may have been partially used.
- CHXSimpleList::Iterator ndx = m_pDataList->Begin();
- for (; ndx != m_pDataList->End(); ++ndx)
- {
- pInfo = (HXAudioInfo*) (*ndx);
- if (pInfo->ulBytesLeft != pInfo->pBuffer->GetSize())
- {
- pInfo->pOffset = pInfo->pBuffer->GetBuffer();
- pInfo->ulBytesLeft = pInfo->pBuffer->GetSize();
- }
- else
- {
- break;
- }
- }
- UINT32 ulLastWriteTime = INT64_TO_UINT32(m_llLastWriteTime - CAST_TO_INT64 m_ulTSRollOver * CAST_TO_INT64 MAX_UINT32);
- BOOL bTimedToBeSet = (m_pLastNMilliSecsList->GetCount() > 0);
- while (m_pLastNMilliSecsList->GetCount() > 0)
- {
- pInfo = (HXAudioInfo*) m_pLastNMilliSecsList->RemoveTail();
- m_pDataList->AddHead(pInfo);
- if (pInfo->ulStartTime <= ulLastWriteTime)
- {
- break;
- }
- }
- if (bTimedToBeSet)
- {
- pInfo = (HXAudioInfo*) m_pDataList->GetHead();
- pInfo->uAudioStreamType = TIMED_AUDIO;
- }
- // remove remaining elements from the list
- while (m_pLastNMilliSecsList->GetCount() > 0)
- {
- pInfo = (HXAudioInfo*) m_pLastNMilliSecsList->RemoveHead();
- // delete the stored one
- HX_RELEASE(pInfo->pBuffer);
- HX_DELETE(pInfo);
- }
- //{FILE* f1 = ::fopen("d:\temp\multi.txt", "a+"); ::fprintf(f1, "%p %p RewindStream %lu %lun", this, m_Owner, ulFirstAudioTime, m_llLastWriteTime);::fclose(f1);}
- #if defined(HELIX_FEATURE_AUDIO_INACCURATESAMPLING)
- // put back stuff from adjusted list to in list
- while (m_pRAByToTsAdjustedList && m_pRAByToTsAdjustedList->GetCount() > 0)
- {
- RealAudioBytesToTimeStamp* pByToTs =
- (RealAudioBytesToTimeStamp*) m_pRAByToTsAdjustedList->RemoveTail();
- // restore original fudge timestamp
- pByToTs->m_ulTimestamp = pByToTs->m_ulOrigTimestamp;
- m_pRAByToTsInList->AddHead(pByToTs);
- }
- #endif /* HELIX_FEATURE_AUDIO_INACCURATESAMPLING */
- }
- }
- void
- CHXAudioStream::Pause(BOOL bPlayerPause)
- {
- if (m_eState == E_PAUSED)
- {
- return;
- }
- m_eState = E_PAUSED;
- m_bCanBeRewound = FALSE;
- m_bPlayerPause = bPlayerPause;
- return;
- }
- void
- CHXAudioStream::Resume(BOOL bPlayerResume)
- {
- if (!m_bInited ||
- m_eState == E_PLAYING)
- {
- return;
- }
- UpdateStreamLastWriteTime();
- // resetting mixengine time line is done in UpdateStreamLastWriteTime()
- // add/resume audio stream on the fly
- if( m_Owner->IsResumed() )
- {
-
- if (m_eState != E_PAUSED &&
- !m_bIsResumed &&
- (!m_pDataList->IsEmpty() || !m_pInstantaneousList->IsEmpty()))
- {
- m_Owner->AudioStreamStateChanged(E_PLAYING);
- m_eState = E_PLAYING;
- }
- // whoever pause the stream is responsible for resuming the same
- // stream, the stream can either be paused specifically by the SMIL renderer
- // without pausing the playback or be paused by the AudioPlayer which
- // pauses the playback
- else if (!bPlayerResume || m_bPlayerPause)
- {
- m_eState = E_PLAYING;
- }
- }
- else
- {
- m_eState = E_PLAYING;
- }
-
- if (m_eState == E_PLAYING)
- {
- m_bCanBeRewound = TRUE;
- m_bIsResumed = TRUE;
- }
-
- return;
- }
- void
- CHXAudioStream::Seek(UINT32 ulSeekTime)
- {
- m_llLastWriteTime = CAST_TO_INT64 (m_ulBaseTime + ulSeekTime);
- // XXX wschildbach: How to account for rollover?
- m_pMixEngine->ResetTimeLineInMillis(m_llLastWriteTime) ;
- m_bFirstWrite = TRUE;
- m_bTobeTimed = TRUE;
- m_ulTSRollOver = 0;
- m_ulLastInputStartTime = 0;
- m_ulLastInputEndTime = 0;
- // Remove all buffers from auxlliary list. This means that a
- // renderer must send all buffers again including those buffers
- // that start at time t way out there in the future.
- FlushBuffers();
- CleanupRAByToTs();
-
- /* Remove any cross-fading */
- m_bCrossFadingToBeDone = FALSE;
- HX_RELEASE(m_pCrossFadeStream);
- /* clear the pending bytes buffer */
- m_ulPendingAudioBytes = 0 ;
- HX_RELEASE(m_piPendingAudioData) ;
- // XXX HP what happen if this is called from the client core on
- // IHXTrack::Seek()
- return;
- }
- void
- CHXAudioStream::Stop(void)
- {
- if (m_eState == E_STOPPED)
- {
- return;
- }
- m_eState = E_STOPPED;
- ResetStream();
- return;
- }
- void
- CHXAudioStream::SetAudioDeviceReflushHint(BOOL bSupported)
- {
- m_bAudioDeviceReflushHint = bSupported;
- return;
- }
- void
- CHXAudioStream::FreeInfo(HXAudioInfo* pInfo, BOOL bInstantaneous /* = FALSE */)
- {
- if (m_bLastNMilliSecsToBeSaved && !bInstantaneous)
- {
- if (!m_pLastNMilliSecsList)
- {
- m_pLastNMilliSecsList = new CHXSimpleList;
- m_ulLastNHeadTime = pInfo->ulStartTime;
- m_ulLastNTailTime = pInfo->ulStartTime;
- }
- // reset members
- pInfo->pOffset = pInfo->pBuffer->GetBuffer();
- pInfo->ulBytesLeft = pInfo->pBuffer->GetSize();
- // add it to the tail
- m_pLastNMilliSecsList->AddTail(pInfo);
- // Last m_ulLastNTailTime could have been invalidated by a rewind:
- // check it again here:
- m_ulLastNHeadTime = ((HXAudioInfo*) m_pLastNMilliSecsList->GetHead())->ulStartTime;
- m_ulLastNTailTime = pInfo->ulStartTime;
- // time to expire certain blocks?
- if (CALCULATE_ELAPSED_TICKS(m_ulLastNHeadTime, m_ulLastNTailTime) > m_ulLastNMilliSeconds)
- {
- // override pInfo. we will delete this block at the bottom
- pInfo = (HXAudioInfo*) m_pLastNMilliSecsList->RemoveHead();
- // update head time
- HXAudioInfo* pHeadInfo = (HXAudioInfo*) m_pLastNMilliSecsList->GetHead();
- // we should always have ATLEAST one nore in the list
- HX_ASSERT(pHeadInfo);
- m_ulLastNHeadTime = pHeadInfo->ulStartTime;
- }
- else
- {
- // early exit to save this block
- return;
- }
- }
- FreeBuffer(pInfo->pBuffer);
- delete pInfo;
- }
- void
- CHXAudioStream::FreeBuffer(IHXBuffer* pBuffer)
- {
- /* do we need to keep it around for reuse? */
- if (!m_pAvailableBuffers || m_pAvailableBuffers->GetCount() >= m_uCacheSize)
- {
- //{FILE* f1 = ::fopen("d:\temp\cache.txt", "a+"); ::fprintf(f1, "Discard n");::fclose(f1);}
- /*
- * now that we are full, we should grow the cache size, if we ever have
- * a cache miss
- */
- m_bCacheMayBeGrown = TRUE;
- pBuffer->Release();
- return;
- }
- /*
- * check if we have the only reference, if so reuse it
- * else release our reference
- */
- pBuffer->AddRef();
- if (pBuffer->Release() > 1)
- {
- pBuffer->Release();
- return;
- }
- #ifdef _MACINTOSH
- m_pAvailableBuffers->AddTail((void*) pBuffer);
- #else
- BOOL bAddToTail = (HX_GET_BETTERTICKCOUNT() & 0x01) ? TRUE : FALSE;
- LISTPOSITION listRet = NULL;
- if (bAddToTail)
- {
- listRet = m_pAvailableBuffers->AddTail((void*) pBuffer);
- }
- else
- {
- listRet = m_pAvailableBuffers->AddHead((void*) pBuffer);
- }
- if( listRet == NULL )
- {
- m_wLastError = HXR_OUTOFMEMORY;
- }
- #endif
- //{FILE* f1 = ::fopen("d:\temp\cache.txt", "a+"); ::fprintf(f1, "Added %dn", m_pAvailableBuffers->GetCount());::fclose(f1);}
- return;
- }
- /************************************************************************
- * Method:
- * IHXCommonClassFactory::CreateInstance
- */
- STDMETHODIMP
- CHXAudioStream::CreateInstance
- (
- REFCLSID /*IN*/ rclsid,
- void** /*OUT*/ ppUnknown
- )
- {
- HX_RESULT theErr = HXR_OK;
- if (IsEqualCLSID(rclsid, CLSID_IHXBuffer))
- {
- if (!m_pAvailableBuffers)
- {
- #ifdef _MACINTOSH
- m_pAvailableBuffers = new HXAudioMacQueue;
- #else
- m_pAvailableBuffers = new CHXSimpleList;
- #endif
- }
- if (m_pAvailableBuffers->GetCount() > 0)
- {
- #ifdef _MACINTOSH
- *ppUnknown = (IUnknown*) (IHXBuffer*) m_pAvailableBuffers->RemoveHead();
- if (!*ppUnknown) goto justincase;
- #else
- BOOL bRemoveFromHead = (HX_GET_BETTERTICKCOUNT() & 0x01) ? TRUE : FALSE;
- if (bRemoveFromHead)
- {
- *ppUnknown = (IUnknown*) (IHXBuffer*) m_pAvailableBuffers->RemoveHead();
- }
- else
- {
- *ppUnknown = (IUnknown*) (IHXBuffer*) m_pAvailableBuffers->RemoveTail();
- }
- #endif
- //{FILE* f1 = ::fopen("d:\temp\cache.txt", "a+"); ::fprintf(f1, "Cache Hit %dn", m_pAvailableBuffers->GetCount());::fclose(f1);}
- goto exit;
- }
- else
- {
- #ifdef _MACINTOSH
- justincase:
- #endif
- if (m_bCacheMayBeGrown)
- {
- m_bCacheMayBeGrown = FALSE;
- m_uCacheSize += CACHE_INCREMENT_SIZE;
- //{FILE* f1 = ::fopen("d:\temp\cache.txt", "a+"); ::fprintf(f1, "Cache increased to: %u n", m_uCacheSize);::fclose(f1);}
- }
- }
- /*
- * fall down to using the comonclass factory to allocate this buiffer since
- * we do not have it in the cache
- */
- }
- //{FILE* f1 = ::fopen("d:\temp\cache.txt", "a+"); ::fprintf(f1, "Cache Miss buffered blocks: %dn", m_pDataList->GetCount());::fclose(f1);}
- theErr = m_pCommonClassFactory->CreateInstance(rclsid, ppUnknown);
- exit:
- return theErr;
- }
- /************************************************************************
- * Method:
- * IHXCommonClassFactory::CreateInstanceAggregatable
- */
- STDMETHODIMP
- CHXAudioStream::CreateInstanceAggregatable
- (
- REFCLSID /*IN*/ rclsid,
- REF(IUnknown*) /*OUT*/ pUnknown,
- IUnknown* /*IN*/ pUnkOuter
- )
- {
- return m_pCommonClassFactory->CreateInstanceAggregatable(rclsid, pUnknown, pUnkOuter);
- }
- // XXX wschildbach: What does this method do? How do we implement this with
- // the 64-bit timestamps?
- /************************************************************************
- * Method:
- * IHXUpdateProperties::UpdatePacketTimeOffset
- * Purpose:
- * Call this method to update the timestamp offset of cached packets
- */
- STDMETHODIMP
- CHXAudioStream::UpdatePacketTimeOffset(INT32 lTimeOffset)
- {
- HX_RESULT rc = HXR_OK;
- HXAudioInfo* pInfo = NULL;
- // adjust the start time
- CHXSimpleList::Iterator ndx = m_pDataList->Begin();
- for (; ndx != m_pDataList->End(); ++ndx)
- {
- pInfo = (HXAudioInfo*) (*ndx);
- pInfo->ulStartTime += lTimeOffset;
- }
-
- if (m_pLastNMilliSecsList)
- {
- ndx = m_pLastNMilliSecsList->Begin();
- for (; ndx != m_pLastNMilliSecsList->End(); ++ndx)
- {
- pInfo = (HXAudioInfo*) (*ndx);
- pInfo->ulStartTime += lTimeOffset;
- }
- }
- // Adjust more state:
- m_ulLastInputStartTime += lTimeOffset;
- m_ulLastInputEndTime += lTimeOffset;
- m_llLastWriteTime += lTimeOffset;
- m_ulLastNHeadTime += lTimeOffset;
- m_ulLastNTailTime += lTimeOffset;
- m_llLastStartTimePlayed += lTimeOffset;
- return rc;
- }
- /************************************************************************
- * Method:
- * IHXUpdateProperties::UpdatePlayTimes
- * Purpose:
- * Call this method to update the playtime attributes
- */
- STDMETHODIMP
- CHXAudioStream::UpdatePlayTimes(IHXValues* pProps)
- {
- return HXR_OK;
- }
- // XXX wschildbach: How do we implement this method with 64-bit timestamps?
- void
- CHXAudioStream::RollBackTimestamp()
- {
- if (m_llLastWriteTime > CAST_TO_INT64 m_ulGranularity)
- {
- m_llLastWriteTime -= CAST_TO_INT64 m_ulGranularity;
- }
- }
- #ifdef _MACINTOSH
- /////////////////////////////////////////////////////////////////////////
- //
- // HXAudioMacQueue
- //
- // For passing data between an interrupt and anything else (mac only).
- //
- HXAudioMacQueue::HXAudioMacQueue()
- {
- mQueueHeader.qFlags=0;
- mQueueHeader.qHead=0;
- mQueueHeader.qTail=0;
- mDestructing = FALSE; // just a safety check
- m_nCount = 0;
- }
- /////////////////////////////////////////////////////////////////////////
- //
- HX_RESULT HXAudioMacQueue::AddTail(void* pObject)
- {
- if (pObject && !mDestructing)
- {
- HXAudioMacQueueElement * theElement = new HXAudioMacQueueElement();
-
- if (theElement)
- {
- theElement->mNextElementInQueue = NULL;
- theElement->mObject = pObject;
- ::Enqueue((QElem *)theElement, &mQueueHeader);
-
- m_nCount++;
-
- //
- // If someone interrupts and enters the destructor while we're in here,
- // then the pObject and the new node will be leaked. This shouldn't
- // happen since we should have shut down all interrupts that would
- // be adding items to the queue long before we start destructing it.
- //
-
- HX_ASSERT(!mDestructing); // if we DID enter the destructor, let the programmer know...
- }
-
- return HXR_OK;
- }
-
- return HXR_FAILED;
- }
- /////////////////////////////////////////////////////////////////////////
- //
- void * HXAudioMacQueue::RemoveHead()
- {
- //
- // POINT A
- //
- // You can look at the qHead anytime you want, but you can't USE a
- // pointer unless it's OFF of the queue. Basically you do a
- // Dequeue, and if it succeeds then you know nobody else has it.
- // If it fails, an error is returned and you don't mess with it.
- //
-
- if (mQueueHeader.qHead)
- {
- HXAudioMacQueueElement * theElement = (HXAudioMacQueueElement *) mQueueHeader.qHead;
- if (theElement)
- {
- OSErr e = ::Dequeue( (QElemPtr) theElement, &mQueueHeader );
-
- //
- // Between points A and D, we can't be
- // guaranteed that the queue header and
- // theElement are valid. But Dequeue will
- // TELL us if that pointer is still valid by
- // its return code. If it can't remove the
- // item from the queue, then somebody else did
- // and the pointer is no longer ours. If no
- // error was returned from dequeue, then it's
- // ours to mess with.
- //
-
- if (e == noErr)
- {
- // at this point we know that we can
- // do whatever we need to with the
- // object.
- void* theObj = theElement->mObject;
- delete theElement; // delete the node
- m_nCount--;
- HX_ASSERT(m_nCount >= 0);
- return theObj;
- }
- }
- }
-
- return NULL;
- }
- /////////////////////////////////////////////////////////////////////////
- //
- UINT32 HXAudioMacQueue::GetCount()
- {
- return m_nCount;
- }
- /////////////////////////////////////////////////////////////////////////
- //
- HXAudioMacQueue::~HXAudioMacQueue()
- {
- mDestructing = TRUE; // don't add anything else to the queue
-
- void * theObject;
- while ((theObject = RemoveHead()) != 0)
- {
- }
-
- // and just to be safe...
- mQueueHeader.qHead=0;
- mQueueHeader.qTail=0;
- }
- #endif