hxaudstr_new.cpp
上传用户:zhongxx05
上传日期:2007-06-06
资源大小:33641k
文件大小:88k
源码类别:

Symbian

开发平台:

C/C++

  1. /* ***** BEGIN LICENSE BLOCK ***** 
  2.  * Version: RCSL 1.0/RPSL 1.0 
  3.  *  
  4.  * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. 
  5.  *      
  6.  * The contents of this file, and the files included with this file, are 
  7.  * subject to the current version of the RealNetworks Public Source License 
  8.  * Version 1.0 (the "RPSL") available at 
  9.  * http://www.helixcommunity.org/content/rpsl unless you have licensed 
  10.  * the file under the RealNetworks Community Source License Version 1.0 
  11.  * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, 
  12.  * in which case the RCSL will apply. You may also obtain the license terms 
  13.  * directly from RealNetworks.  You may not use this file except in 
  14.  * compliance with the RPSL or, if you have a valid RCSL with RealNetworks 
  15.  * applicable to this file, the RCSL.  Please see the applicable RPSL or 
  16.  * RCSL for the rights, obligations and limitations governing use of the 
  17.  * contents of the file.  
  18.  *  
  19.  * This file is part of the Helix DNA Technology. RealNetworks is the 
  20.  * developer of the Original Code and owns the copyrights in the portions 
  21.  * it created. 
  22.  *  
  23.  * This file, and the files included with this file, is distributed and made 
  24.  * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  25.  * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  26.  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS 
  27.  * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
  28.  * 
  29.  * Technology Compatibility Kit Test Suite(s) Location: 
  30.  *    http://www.helixcommunity.org/content/tck 
  31.  * 
  32.  * Contributor(s): 
  33.  *  
  34.  * ***** END LICENSE BLOCK ***** */ 
  35. #include "hlxclib/stdio.h" 
  36. #include "hlxclib/string.h"
  37. #include "hxresult.h"
  38. #include "hxtypes.h"
  39. #include "hxcom.h"
  40. #include "hxengin.h"
  41. #include "ihxpckts.h"
  42. #include "hxbuffer.h"
  43. #include "hxausvc.h"
  44. #include "hxrasyn.h"
  45. #include "hxprefs.h"
  46. #include "hxerror.h"
  47. #include "errdbg.h"
  48. #include "chxpckts.h"
  49. #include "hxaudply.h"
  50. #include "hxaudstr.h"
  51. #include "hxaudses.h"
  52. #include "hxaudvol.h"
  53. #include "mixengine.h"
  54. #include "hxslist.h"
  55. #include "hxmap.h"
  56. #include "auderrs.h"
  57. #include "hxtick.h"
  58. #include "hxheap.h"
  59. #ifdef _DEBUG
  60. #undef HX_THIS_FILE
  61. static const char HX_THIS_FILE[] = __FILE__;
  62. #endif
  63. #define CACHE_INCREMENT_SIZE 2
  64. //#define _TESTING    1
  65. #ifdef _TESTING
  66. #include <fcntl.h>
  67. #include <sys/types.h>
  68. #include <sys/stat.h>
  69. #if defined (_WINDOWS) || defined (_WIN32)
  70. #include <io.h>
  71. #endif
  72. int g_log = -1;
  73. #endif
  74. /************************************************************************
  75.  *  Method:
  76.  * IHXAudioStream::CHXAudioStream()
  77.  * Purpose:
  78.  * Constructor. 
  79.  */
  80. CHXAudioStream::CHXAudioStream(CHXAudioPlayer* owner, IUnknown* pContext)
  81. :       m_lRefCount(0)
  82. ,       m_wLastError(HXR_OK)
  83. ,       m_pResampler(NULL)
  84. ,       m_pValues(0)
  85. ,       m_bDisableWrite(FALSE)
  86. ,       m_ulGranularity(0)
  87. , m_ulInputBytesPerGran(0)
  88. , m_ulOutputBytesPerGran(0)
  89. ,       m_pDataList(0)
  90. , m_pInstantaneousList(0)
  91. , m_pRAByToTsInList(0)
  92. , m_pRAByToTsAdjustedList(0)
  93. ,       m_bFirstWrite(TRUE)
  94. , m_bHooksInitialized(FALSE)
  95. ,       m_bInited(FALSE)
  96. , m_bSetupDone(FALSE)
  97. , m_bAudioFormatKnown(FALSE)
  98. ,       m_ulMaxBlockSize(0)
  99. ,       m_uVolume(HX_MAX_VOLUME)
  100. , m_bMute(FALSE)
  101. ,       m_bGotHooks(FALSE)
  102. , m_llLastWriteTime(0)
  103. , m_ulFudge(5)
  104. , m_pInDataPtr(0)
  105. ,       m_pOutDataPtr(0)
  106. , m_bTobeTimed(TRUE)
  107. , m_bIsFirstPacket(TRUE)
  108. , m_bIsLive(FALSE)
  109. , m_ulBaseTime(0)
  110. , m_ulLiveDelay(0)
  111. , m_bSetupToBeDone(FALSE)
  112. ,       m_bCrossFadingToBeDone(FALSE)
  113. ,       m_pCrossFadeStream(NULL)
  114. ,       m_llCrossFadeStartTime(0)
  115. ,       m_ulCrossFadeDuration(0)
  116. ,       m_bFadeToThisStream(FALSE)
  117. , m_bFadeAlreadyDone(FALSE)
  118. , m_bRealAudioStream(FALSE)
  119. , m_ulLastInputStartTime(0)
  120. , m_ulLastInputEndTime(0)
  121. , m_llLastStartTimePlayed(0)
  122. , m_ulTSRollOver(0)
  123. , m_bLastWriteTimeUpdated(FALSE)
  124. , m_pCommonClassFactory(NULL)
  125. , m_pAvailableBuffers(NULL)
  126. , m_uCacheSize(CACHE_INCREMENT_SIZE)
  127. , m_bCacheMayBeGrown(FALSE)
  128. , m_bDeterminedInitialCacheSize(FALSE)
  129. , m_bLastNMilliSecsToBeSaved(FALSE)
  130. , m_ulLastNMilliSeconds(MINIMUM_INITIAL_PUSHDOWN)
  131. , m_pLastNMilliSecsList(NULL)
  132. , m_ulLastNHeadTime(0)
  133. , m_ulLastNTailTime(0)
  134. ,  m_eState(E_STOPPED)
  135. , m_bCanBeRewound(FALSE)
  136. , m_bAudioDeviceReflushHint(FALSE)
  137. , m_bIsResumed(FALSE)
  138. , m_bPlayerPause(FALSE)
  139. ,       m_pPreferences(NULL)
  140. , m_bMayNeedToRollbackTimestamp(FALSE)
  141. , m_piPendingAudioData(NULL)
  142. {
  143.     m_Owner     = owner;
  144.     if (m_Owner)
  145.     {
  146. m_Owner->AddRef();
  147.     }
  148.     if (pContext)
  149.     {
  150. HX_VERIFY(HXR_OK == pContext->QueryInterface(IID_IHXCommonClassFactory, 
  151.     (void**) &m_pCommonClassFactory));
  152.     }
  153. #ifdef HELIX_FEATURE_VOLUME
  154.     m_pStreamVolume = NULL;
  155. #endif
  156.     
  157. #if defined(HELIX_FEATURE_PREFERENCES)
  158.     if (pContext)
  159.     {
  160. HX_VERIFY(HXR_OK == pContext->QueryInterface(IID_IHXPreferences, (void**) &m_pPreferences));
  161.     }
  162. #endif /* HELIX_FEATURE_PREFERENCES */
  163.     m_DryNotificationMap = new CHXMapPtrToPtr;
  164.     m_pInDataPtr    = new HXAudioData;
  165.     m_pOutDataPtr   = new HXAudioData;
  166.     m_pMixEngine    = new HXAudioSvcMixEngine() ;
  167. };
  168. /************************************************************************
  169.  *  Method:
  170.  * IHXAudioStream::~CHXAudioStream()
  171.  * Purpose:
  172.  * Destructor. Clean up and set free.
  173.  */
  174. CHXAudioStream::~CHXAudioStream()
  175. {
  176.     HX_DELETE(m_DryNotificationMap);
  177.     ResetStream();
  178.     HX_RELEASE(m_piPendingAudioData);
  179. }
  180.  
  181. /////////////////////////////////////////////////////////////////////////
  182. //  Method:
  183. //      IUnknown::QueryInterface
  184. //  Purpose:
  185. //      Implement this to export the interfaces supported by your
  186. //      object.
  187. //
  188. STDMETHODIMP CHXAudioStream::QueryInterface(REFIID riid, void** ppvObj)
  189. {
  190.     QInterfaceList qiList[] =
  191.         {
  192.             { GET_IIDHANDLE(IID_IHXAudioStream), (IHXAudioStream*)this },
  193.             { GET_IIDHANDLE(IID_IHXRealAudioSync), (IHXRealAudioSync*)this },
  194.             { GET_IIDHANDLE(IID_IHXAudioStream2), (IHXAudioStream2*)this },
  195.             { GET_IIDHANDLE(IID_IHXCommonClassFactory), (IHXCommonClassFactory*)this },
  196.             { GET_IIDHANDLE(IID_IHXUpdateProperties), (IHXUpdateProperties*)this },
  197.             { GET_IIDHANDLE(IID_IUnknown), (IUnknown*)(IHXAudioStream*)this },
  198. #ifdef HELIX_FEATURE_VOLUME            
  199.             { GET_IIDHANDLE(IID_IHXVolumeAdviseSink), (IHXVolumeAdviseSink*)this },
  200. #endif            
  201.         };
  202.     
  203.     return ::QIFind(qiList, QILISTSIZE(qiList), riid, ppvObj);
  204. }
  205. /////////////////////////////////////////////////////////////////////////
  206. //  Method:
  207. //      IUnknown::AddRef
  208. //  Purpose:
  209. //      Everyone usually implements this the same... feel free to use
  210. //      this implementation.
  211. //
  212. STDMETHODIMP_(ULONG32) CHXAudioStream::AddRef()
  213. {
  214.     return InterlockedIncrement(&m_lRefCount);
  215. }
  216. /////////////////////////////////////////////////////////////////////////
  217. //  Method:
  218. //      IUnknown::Release
  219. //  Purpose:
  220. //      Everyone usually implements this the same... feel free to use
  221. //      this implementation.
  222. //
  223. STDMETHODIMP_(ULONG32) CHXAudioStream::Release()
  224. {
  225.     if (InterlockedDecrement(&m_lRefCount) > 0)
  226.     {
  227.         return m_lRefCount;
  228.     }
  229.     delete this;
  230.     return 0;
  231. }
  232. /*
  233.  *  IHXAudioStream methods
  234.  */
  235. /************************************************************************
  236.  *  Method:
  237.  * IHXAudioStream::Init
  238.  * Purpose:
  239.  * Init the audio stream.
  240.  */
  241. STDMETHODIMP CHXAudioStream::Init
  242. (
  243.     const HXAudioFormat* pAudioFormat,
  244.           IHXValues* pValues
  245. )
  246. {
  247.     if (m_bAudioFormatKnown)
  248.     {
  249. return HXR_OK;
  250.     }
  251.     HX_RESULT theErr = HXR_OK;
  252.     m_pValues = pValues;
  253.     if (m_pValues)
  254.     {
  255. m_pValues->AddRef();
  256. UINT32 ulVal = 0;
  257. m_pValues->GetPropertyULONG32("audioDeviceReflushHint", ulVal);
  258. if (ulVal == 1)
  259. {
  260.     SetAudioDeviceReflushHint(TRUE);
  261.     m_Owner->m_Owner->CheckIfLastNMilliSecsToBeStored();
  262. }
  263.     }
  264.     memcpy( &m_AudioFmt, pAudioFormat, sizeof(HXAudioFormat) );
  265.     // Create the audio data list 
  266.     m_pDataList = new CHXSimpleList;
  267.     if ( !m_pDataList )
  268.         theErr = HXR_OUTOFMEMORY;
  269.     if(!theErr) // check if list constructor really succeeded
  270.     {
  271.         if(!m_pDataList->IsPtrListValid())
  272.             theErr = HXR_OUTOFMEMORY;
  273.     }
  274.     m_pInstantaneousList = new CHXSimpleList;
  275.     if ( !m_pInstantaneousList || !m_pInstantaneousList->IsPtrListValid())
  276.         theErr = HXR_OUTOFMEMORY;
  277.     // Reset this so that we init the hooks
  278.     m_bFirstWrite = TRUE;
  279.     m_bHooksInitialized = FALSE;
  280. #ifdef HELIX_FEATURE_VOLUME    
  281.     if( !theErr )
  282.     {
  283.         m_pStreamVolume = (IHXVolume*)new CHXVolume;
  284.         if( m_pStreamVolume )
  285.         {
  286.             m_pStreamVolume->AddRef();
  287.             m_pStreamVolume->AddAdviseSink(this);
  288.         }
  289.         else
  290.             theErr = HXR_OUTOFMEMORY;
  291.     }
  292. #endif    
  293.     m_bAudioFormatKnown = TRUE;
  294.     if (m_bSetupToBeDone)
  295.     {
  296. m_bSetupToBeDone    = FALSE;
  297. m_Owner->AudioFormatNowKnown();
  298.     }
  299.     if (!theErr && m_bSetupDone && !m_bInited)
  300.     {
  301. theErr = ProcessInfo();
  302.     }
  303.     return theErr;
  304. }
  305. /************************************************************************
  306.  *  Method:
  307.  * IHXAudioStream::Write
  308.  *  Purpose:
  309.  *      Write audio data to Audio Services. 
  310.  *
  311.  *      NOTE: If the renderer loses packets and there is no loss
  312.  *      correction, then the renderer should write the next packet 
  313.  *      using a meaningful start time.  Audio Services will play 
  314.  *      silence where packets are missing.
  315.  */
  316. STDMETHODIMP CHXAudioStream::Write
  317.     HXAudioData* pInData
  318. )
  319. {
  320.     HX_RESULT theErr = HXR_OK;
  321.     if (!pInData)
  322.     {
  323. return HXR_INVALID_PARAMETER;
  324.     }
  325.     if (!m_bInited)
  326.     {
  327. return HXR_NOT_INITIALIZED;
  328.     }
  329.     // Init pre-mix hooks. Call this once to set up hook info. 
  330.     if ( !m_bHooksInitialized )
  331.     {
  332. InitHooks();
  333.     }
  334. #if defined(HELIX_FEATURE_AUDIO_INCOMPLETESAMPLE)
  335.     /* make sure that we are always handing complete sample frames
  336.      * down the chain by buffering up samples if we don't get complete frames.
  337.      *
  338.      * This is done by using a slush IHXBuffer that is just large enough
  339.      * to hold one sample frame, and a second IHXBuffer to hold sample fragments.
  340.      * This one does not own a buffer, but uses the incoming buffers.
  341.      *
  342.      * This way, we don't need any large memcpy()s. Unfortunately, we have to
  343.      * create a new slush buffer every time because we don't know how long
  344.      * this buffer will be stuck in the queue until it gets rendered.
  345.      *
  346.      * I guess we could try to be smart and only create a new one if the old one
  347.      * is still AddRef()ed but I figure it's not worth it.
  348.      */
  349.     /* if there was a discontinuity in the audio stream, throw away pending bytes.
  350.      * This also assumes that we're starting at a sample frame boundary, which might
  351.      * be wrong -- but we really have no way to tell.
  352.      */
  353.     if (pInData->uAudioStreamType != STREAMING_AUDIO)
  354.     {
  355. m_ulPendingAudioBytes = 0 ;
  356. HX_RELEASE(m_piPendingAudioData) ;
  357.     }
  358.     /* if pData is NULL, hand it through unchanged. See comment in Write2() */
  359.     if (!pInData->pData)
  360. return Write2(pInData) ;
  361.     UINT32 ulInBytes = pInData->pData->GetSize() ; // number of bytes in incoming sample
  362.     UINT32 ulCutoffBytes = 0 ; // number of bytes that will be cut off incoming sample
  363.     /* first check if we have pending samples. */
  364.     if (m_ulPendingAudioBytes)
  365.     {
  366. // complete sample frames would have been sent the last time around
  367. HX_ASSERT(m_ulPendingAudioBytes < m_ulSampleFrameSize) ;
  368. HX_ASSERT(m_piPendingAudioData) ;
  369. /* append more bytes from the start of the incoming packet. */
  370. ulCutoffBytes = m_ulSampleFrameSize - m_ulPendingAudioBytes ;
  371. if (ulCutoffBytes > ulInBytes)
  372.     ulCutoffBytes = ulInBytes ;
  373. memcpy(m_piPendingAudioData->GetBuffer() + m_ulPendingAudioBytes,
  374.        pInData->pData->GetBuffer(),
  375.        ulCutoffBytes ) ;
  376. m_ulPendingAudioBytes += ulCutoffBytes ;
  377. ulInBytes             -= ulCutoffBytes ;
  378.     }
  379.     // if we have a complete sample frame in the slush buffer, send it.
  380.     if (m_ulPendingAudioBytes == m_ulSampleFrameSize)
  381.     {
  382. HX_ASSERT(m_piPendingAudioData) ;
  383. HXAudioData audioData ;
  384. audioData.pData = m_piPendingAudioData ;
  385. audioData.pData->AddRef() ;
  386. // use time of incoming packet -- one sample frame is below the
  387. // ms resolution of time stamps.
  388. audioData.ulAudioTime = pInData->ulAudioTime;
  389. // stream type is incoming stream type
  390. audioData.uAudioStreamType = pInData->uAudioStreamType;
  391. theErr = Write2(&audioData) ;
  392. audioData.pData->Release() ;
  393. m_ulPendingAudioBytes = 0 ;
  394. // release the slush buffer
  395. HX_RELEASE(m_piPendingAudioData) ;
  396. if (FAILED(theErr))
  397.     return theErr ;
  398.     }
  399.     // put partial sample frames from the end of the incoming buffer
  400.     // into the slush buffer.
  401.     if (ulInBytes % m_ulSampleFrameSize)
  402.     {
  403. // the slush buffer should be empty here.
  404. HX_ASSERT(m_ulPendingAudioBytes == 0);
  405. HX_ASSERT(m_piPendingAudioData == 0) ;
  406. // reserve a new slush buffer
  407. theErr = CreateInstance(IID_IHXBuffer, (void**)&m_piPendingAudioData);
  408. if (SUCCEEDED(theErr))
  409.     theErr = m_piPendingAudioData->SetSize(m_ulSampleFrameSize) ;
  410. if (SUCCEEDED(theErr))
  411. {
  412.     m_ulPendingAudioBytes = ulInBytes % m_ulSampleFrameSize ;
  413.     ulInBytes -= m_ulPendingAudioBytes ;
  414.     memcpy(m_piPendingAudioData->GetBuffer(),
  415.    pInData->pData->GetBuffer() + ulCutoffBytes + ulInBytes,
  416.    m_ulPendingAudioBytes) ;
  417. }
  418. if (FAILED(theErr))
  419.     return theErr ;
  420.     }
  421.     // send any leftover fragment of the incoming buffer.
  422.     if (ulInBytes == pInData->pData->GetSize() && !ulCutoffBytes)
  423.     /* this is the entire buffer, not a fragment. 
  424.      * This is the normal case -- let's handle it efficiently. */
  425.     {
  426. theErr = Write2(pInData) ;
  427.     }
  428.     else if (ulInBytes)
  429.     /* if anything left in buffer, send it in a fragment */
  430.     {
  431. HXAudioData audioData ;
  432. CHXBufferFragment* pFragment = new CHXBufferFragment(
  433.     pInData->pData,
  434.     pInData->pData->GetBuffer() + ulCutoffBytes,
  435.     ulInBytes);
  436. theErr = pFragment->QueryInterface(IID_IUnknown, (void**)&audioData.pData) ;
  437. // this must always succeed, since we know it exports a IHXBuffer
  438. HX_ASSERT(SUCCEEDED(theErr)) ;
  439. // use time of incoming packet -- one sample frame is below the
  440. // ms resolution of time stamps.
  441. audioData.ulAudioTime = pInData->ulAudioTime;
  442. // stream type is incoming stream type if we did not cut anything away,
  443. // and STREAMED_AUDIO if we did (because in that case this is a continuation)
  444. audioData.uAudioStreamType = ulCutoffBytes ? STREAMING_AUDIO : pInData->uAudioStreamType ;
  445. theErr = Write2(&audioData) ;
  446. // we release our hold on pFragment here. When MixIntoBuffer() is done with
  447. // this fragment, it will also release its hold, and the fragment gets
  448. // deleted.
  449. audioData.pData->Release() ;
  450.     }
  451. #else
  452.     theErr = Write2(pInData) ;
  453. #endif /* HELIX_FEATURE_AUDIO_INCOMPLETESAMPLE */
  454.     return theErr;
  455. }
  456. /************************************************************************
  457.  *  Method:
  458.  * IHXAudioStream::Write2
  459.  *  Purpose:
  460.  *      Write audio data to Audio Services. This is a companion/backend
  461.  *      function to IHXAudioStream::Write
  462.  *
  463.  */
  464. HX_RESULT CHXAudioStream::Write2(HXAudioData* pInData)
  465. {
  466.     HX_RESULT theErr = HXR_OK;
  467.     // Process any "hooks"; Add the data to the data list.
  468.     /* If buffer is NULL, it means that the user just 
  469.      * wants to know what timestamp should be placed in the next 
  470.      * STREAMED/TIMED audio data
  471.      */
  472.     if ( !m_bGotHooks || !pInData->pData)
  473.     {
  474. theErr = AddData( pInData );
  475.     }
  476.     else
  477.     {
  478.   HXAudioData outData;
  479. outData.pData     = 0;
  480. outData.ulAudioTime = 0;
  481. theErr = ProcessHooks( pInData, &outData );
  482. if (!theErr && !m_bDisableWrite )
  483. {
  484.     theErr = AddData( &outData );
  485. }
  486. if (outData.pData)
  487. {
  488.     outData.pData->Release();
  489. }
  490.     }
  491.     
  492.     return theErr;
  493. }
  494. /************************************************************************
  495.  *  Method:
  496.  * IHXAudioStream::AddPreMixHook
  497.  * Purpose:
  498.  *      Use this method to add a pre-mix audio data hook.
  499.  */
  500. STDMETHODIMP CHXAudioStream::AddPreMixHook
  501.   IHXAudioHook*    pHook,
  502.     const BOOL              bDisableWrite
  503. )
  504. {
  505. #if defined(HELIX_FEATURE_AUDIO_PREMIXHOOK)
  506.     void* pTmp = 0;
  507.     
  508.     /* Does one already exists */
  509.     if (m_PreMixHookMap.Lookup((void*)pHook, pTmp))
  510.     {
  511. return HXR_INVALID_PARAMETER;
  512.     }
  513.     HXAudioHookInfo* pPreMixHookInfo = (HXAudioHookInfo*) new HXAudioHookInfo;
  514.     pPreMixHookInfo->pHook        = pHook;
  515.     pPreMixHookInfo->bDisableWrite  = bDisableWrite;
  516.     pPreMixHookInfo->bFinal = FALSE;
  517.     pPreMixHookInfo->bIgnoreAudioData = FALSE;
  518.     pPreMixHookInfo->bMultiChannelSupport = FALSE;
  519.     IHXValues* pValues = NULL;
  520.     if (pHook && pHook->QueryInterface(IID_IHXValues, (void**) &pValues) == HXR_OK)
  521.     {
  522. UINT32 ulValue = 0;
  523. pValues->GetPropertyULONG32("IgnoreAudioData", ulValue);
  524. pPreMixHookInfo->bIgnoreAudioData = (ulValue == 1);
  525. HX_RELEASE(pValues);
  526.     }
  527.     pHook->AddRef(); // Released in destructor
  528.     IHXAudioMultiChannel* pMultiChannel = NULL;
  529.     if (pHook && HXR_OK == pHook->QueryInterface(IID_IHXAudioMultiChannel, (void**) &pMultiChannel))
  530.     {
  531.         pPreMixHookInfo->bMultiChannelSupport = pMultiChannel->GetMultiChannelSupport();
  532.     }
  533.     HX_RELEASE(pMultiChannel);
  534.     m_PreMixHookMap.SetAt(pHook, pPreMixHookInfo);
  535.     m_bGotHooks = TRUE;
  536.     /* If any one of them is Disabled, we do not write */
  537.     if (bDisableWrite)
  538.     {
  539. m_bDisableWrite = TRUE;
  540.     }
  541.     ProcessAudioHook(ACTION_ADD, pHook);
  542.     /* If we are already initialized, send the audio format */
  543.     if (m_bHooksInitialized)
  544.     {
  545. if (pPreMixHookInfo->bIgnoreAudioData ||
  546.     HXR_OK == ProcessAudioHook(ACTION_CHECK, pHook))
  547. {
  548.     pHook->OnInit( &m_AudioFmt );
  549. }
  550.     }
  551.     return HXR_OK;
  552. #else
  553.     return HXR_NOTIMPL;
  554. #endif /* HELIX_FEATURE_AUDIO_PREMIXHOOK */
  555. }
  556. /************************************************************************
  557.  *  Method:
  558.  * IHXAudioStream::RemovePreMixHook
  559.  * Purpose:
  560.  *      Use this method to remove a pre-mix audio data hook.
  561.  */
  562. STDMETHODIMP CHXAudioStream::RemovePreMixHook
  563.       IHXAudioHook*    pHook
  564. )
  565. {
  566. #if defined(HELIX_FEATURE_AUDIO_PREMIXHOOK)
  567.     HXAudioHookInfo* pPreMixHookInfo = 0;
  568.     BOOL bCheckForDisableWrite       = FALSE;
  569.     if (!m_PreMixHookMap.Lookup((void*)pHook, (void*&) pPreMixHookInfo))
  570.     {
  571. return HXR_INVALID_PARAMETER;
  572.     }
  573.     m_PreMixHookMap.RemoveKey(pHook);
  574.     /* If we are removing a hook which had disable write, 
  575.      * we need to re-determine if any of the remaining hooks
  576.      * has DisableWrite set to TRUE
  577.      */
  578.     if (pPreMixHookInfo->bDisableWrite)
  579.     {
  580. bCheckForDisableWrite = TRUE;
  581. m_bDisableWrite       = FALSE;
  582.     }
  583.     ProcessAudioHook(ACTION_REMOVE, pHook);
  584.     pPreMixHookInfo->pHook->Release();
  585.     delete pPreMixHookInfo;
  586.     if (m_PreMixHookMap.GetCount() == 0)
  587.     {
  588. m_bGotHooks = FALSE;
  589. m_bDisableWrite = FALSE;
  590.     }
  591.     else if (bCheckForDisableWrite)
  592.     {
  593. CHXMapPtrToPtr::Iterator lIter = m_PreMixHookMap.Begin();
  594. for (; lIter != m_PreMixHookMap.End(); ++lIter)
  595. {
  596.     HXAudioHookInfo* pPreMixHook = (HXAudioHookInfo*) (*lIter);
  597.     
  598.     /* atleast one has Disable Write ON */
  599.     if (pPreMixHook->bDisableWrite)
  600.     {
  601. m_bDisableWrite = TRUE;
  602. break;
  603.     }
  604. }
  605.     }
  606. #endif /* HELIX_FEATURE_AUDIO_PREMIXHOOK */
  607.     return HXR_OK;
  608. }
  609. /************************************************************************
  610. *  Method:
  611. * IHXAudioStream::AddDryNotification
  612. *  Purpose:
  613. * Use this to add a notification response object to get notifications
  614. * when audio stream is running dry.
  615. */
  616. STDMETHODIMP CHXAudioStream::AddDryNotification
  617. (
  618.     IHXDryNotification* /*IN*/ pNotification
  619. )
  620. {
  621.     if (!pNotification)
  622.     {
  623. return HXR_INVALID_PARAMETER;
  624.     }
  625.     void* pTmp = 0;
  626.     
  627.     /* Does one already exists */
  628.     if (m_DryNotificationMap->Lookup((void*)pNotification, pTmp))
  629.     {
  630. return HXR_INVALID_PARAMETER;
  631.     }
  632.     pNotification->AddRef();
  633.     m_DryNotificationMap->SetAt((void*)pNotification, (void*)pNotification);
  634.     return HXR_OK;
  635. }
  636. /************************************************************************
  637. *  Method:
  638. *      IHXAudioStream2::RemoveDryNotification
  639. *  Purpose:
  640. *     Use this to remove itself from the notification response object
  641. *     during the stream switching.
  642. */
  643. STDMETHODIMP CHXAudioStream::RemoveDryNotification   
  644.     (
  645. IHXDryNotification* /*IN*/ pNotification
  646.     )
  647. {
  648.     HX_RESULT hr = HXR_OK;
  649.     void* pTmp = 0;
  650.     if (!pNotification)
  651.     {
  652. hr = HXR_INVALID_PARAMETER;
  653. goto cleanup;
  654.     }
  655.     // remove only if it is exists
  656.     if (m_DryNotificationMap->Lookup((void*)pNotification, pTmp))
  657.     {
  658. m_DryNotificationMap->RemoveKey((void*)pNotification);
  659. HX_RELEASE(pNotification);
  660.     }
  661.     else
  662.     {
  663. hr = HXR_INVALID_PARAMETER;
  664. goto cleanup;
  665.     }
  666. cleanup:
  667.     return hr;
  668. }
  669. /************************************************************************
  670. *  Method:
  671. *      IHXAudioStream2::GetAudioFormat
  672. *  Purpose:
  673. *     Returns the input audio format of the data written by the 
  674. *     renderer. This function will fill in the pre-allocated 
  675. *     HXAudioFormat structure passed in.
  676. */
  677. STDMETHODIMP
  678. CHXAudioStream::GetAudioFormat(HXAudioFormat* /*IN/OUT*/pAudioFormat)
  679. {
  680.     HX_ASSERT(pAudioFormat);
  681.     if (!pAudioFormat)
  682.     {
  683. return HXR_INVALID_PARAMETER;
  684.     }
  685.     if (!m_bInited)
  686.     {
  687. return HXR_UNEXPECTED;
  688.     }
  689.     pAudioFormat->uChannels     = m_AudioFmt.uChannels;
  690.     pAudioFormat->uBitsPerSample    = m_AudioFmt.uBitsPerSample;
  691.     pAudioFormat->ulSamplesPerSec   = m_AudioFmt.ulSamplesPerSec;
  692.     pAudioFormat->uMaxBlockSize     = m_AudioFmt.uMaxBlockSize;
  693.     return HXR_OK;
  694. }
  695.  
  696. /************************************************************************
  697.  *  Method:
  698.  *              IHXAudioStream::GetAudioVolume
  699.  *      Purpose:
  700.  *              Return this stream's IRMA volume interface.
  701.  */
  702. STDMETHODIMP_(IHXVolume*) CHXAudioStream::GetAudioVolume()
  703. {
  704.     IHXVolume* pRet = NULL;
  705.     
  706. #ifdef HELIX_FEATURE_VOLUME    
  707.     if( m_pStreamVolume )
  708.     {
  709.         m_pStreamVolume->AddRef();
  710.         pRet = m_pStreamVolume;
  711.     }
  712. #endif 
  713.    
  714.     return pRet;
  715. }
  716. #if defined(HELIX_FEATURE_VOLUME)
  717. //
  718. // IHXVolume methods
  719. //
  720. STDMETHODIMP CHXAudioStream::OnVolumeChange(const UINT16 uVolume)
  721. {
  722.     m_uVolume = uVolume;
  723. #ifdef HELIX_FEATURE_GAINTOOL
  724.     if (m_pMixEngine)
  725.         m_pMixEngine->SetVolume(m_pMixEngine->HXVolume2TenthOfDB(m_uVolume)) ; 
  726. #endif
  727.     return HXR_OK;
  728. }
  729. STDMETHODIMP CHXAudioStream::OnMuteChange(const BOOL bMute)
  730. {
  731.     m_bMute = bMute;
  732. #ifdef HELIX_FEATURE_GAINTOOL
  733.     if (m_pMixEngine)
  734.         m_pMixEngine->SetVolume(m_pMixEngine->HXVolume2TenthOfDB(bMute ? HX_MIN_VOLUME : m_uVolume)) ;
  735. #endif
  736.     return HXR_OK;
  737. }
  738. #endif /* HELIX_FEATURE_VOLUME */
  739. /************************************************************************
  740.  *  Method:
  741.  * IHXAudioStream::AddData
  742.  * Purpose:
  743.  * Add audio data to list.
  744.  * NOTE: Mark Streamed data also as Timed data IF we don't write a streamed packet
  745.  *  since it was LATE!!!
  746.  */
  747. HX_RESULT CHXAudioStream::AddData
  748. (
  749.     HXAudioData* pAudioData
  750. )
  751. {
  752.     HX_RESULT     theErr = HXR_OK;
  753.     BOOL     bInTSRollOver = FALSE;
  754.     HXAudioInfo*    pAinfo = 0;
  755.     /* If buffer is NULL, it means that the user just 
  756.      * wants to know what timestamp should be placed in the next 
  757.      * STREAMED/TIMED audio data
  758.      */
  759.     if (!pAudioData->pData)
  760.     {
  761. HXAudioInfo* pInfo = NULL;
  762. if (!m_pDataList->IsEmpty() && 
  763.     NULL != (pInfo = (HXAudioInfo*) m_pDataList->GetTail()))
  764. {
  765.     pAudioData->ulAudioTime = pInfo->ulStartTime + 
  766.       CalcMs(pInfo->pBuffer->GetSize());
  767. }
  768. else
  769. {
  770.     pAudioData->ulAudioTime = INT64_TO_UINT32(m_llLastWriteTime - CAST_TO_INT64 (m_ulTSRollOver) * CAST_TO_INT64 (MAX_UINT32));
  771. }
  772. return HXR_OK;
  773.     }
  774.     // make sure the renderer does not pass NULL data!!
  775.     HX_ASSERT(pAudioData->pData->GetBuffer() != NULL &&
  776.       pAudioData->pData->GetSize() != 0);
  777.     if (pAudioData->pData->GetBuffer() == NULL ||
  778. pAudioData->pData->GetSize() == 0)
  779.     {
  780. return HXR_INVALID_PARAMETER;
  781.     }
  782.     if (m_bIsFirstPacket)
  783.     {
  784. m_bIsFirstPacket = FALSE;
  785. IHXErrorMessages* pErrMsg = NULL;
  786. if (HXR_OK == m_Owner->m_pContext->QueryInterface(IID_IHXErrorMessages, (void**)&pErrMsg))
  787. {
  788.     DEBUG_OUT(pErrMsg, DOL_GENERIC, (s,"AudioFormatIn: %lu channels %lu SamplesPerSec", m_AudioFmt.uChannels, m_AudioFmt.ulSamplesPerSec));
  789.     DEBUG_OUT(pErrMsg, DOL_GENERIC, (s,"AudioFormatOut: %lu channels %lu SamplesPerSec", m_DeviceFmt.uChannels, m_DeviceFmt.ulSamplesPerSec));
  790. }
  791. HX_RELEASE(pErrMsg);
  792. if (m_bIsLive)
  793. {
  794.             // XXX wschildbach why do we update a last write time *here*?
  795.     m_Owner->UpdateStreamLastWriteTime();
  796.     UpdateStreamLastWriteTime(TRUE);
  797. }
  798.     }
  799. //{FILE* f1 = ::fopen("c:\temp\audio.txt", "a+"); ::fprintf(f1, "%lutAddDatat%pt%lun", HX_GET_BETTERTICKCOUNT(), this, pAudioData->ulAudioTime);::fclose(f1);}
  800. //    ::fwrite(pAudioData->pData->GetBuffer(), pAudioData->pData->GetSize(), 1, fdin);
  801.     UINT32 ulDataTime = CalcMs(pAudioData->pData->GetSize());
  802.     UINT32 ulEndTime = pAudioData->ulAudioTime + ulDataTime;
  803.     if (m_pAvailableBuffers && !m_bDeterminedInitialCacheSize && ulDataTime > 0)
  804.     {
  805. m_bDeterminedInitialCacheSize = TRUE;
  806. m_uCacheSize = (UINT16) (m_ulGranularity*2/ulDataTime) + 1;
  807. /* make sure it is atleast CACHE_INCREMENT_SIZE to begin with */
  808. m_uCacheSize = m_uCacheSize < CACHE_INCREMENT_SIZE ? 
  809.     CACHE_INCREMENT_SIZE : m_uCacheSize;
  810.     }
  811.     if (m_ulLastInputStartTime > pAudioData->ulAudioTime &&
  812. ((m_ulLastInputStartTime - pAudioData->ulAudioTime) > MAX_TIMESTAMP_GAP))
  813.     {
  814. bInTSRollOver = TRUE;
  815. m_ulTSRollOver++;
  816.     }
  817.     m_ulLastInputStartTime  = pAudioData->ulAudioTime; 
  818.     m_ulLastInputEndTime    = ulEndTime;
  819.     /* even in STREAMING_AUDIO case, it might happen, that the packets
  820.      * written are late. e.g. packets received late on the network 
  821.      */
  822.     // XXX wschildbach : Why do we even care? Just insert the packet into the queue
  823.     // and let the reaper take care of it!
  824.     INT64 llActualTimestamp = CAST_TO_INT64 (pAudioData->ulAudioTime) + CAST_TO_INT64 m_ulTSRollOver * CAST_TO_INT64 MAX_UINT32;
  825.     INT64 llActualEndTime = CAST_TO_INT64 (pAudioData->ulAudioTime) + CAST_TO_INT64 (ulDataTime) +
  826.     CAST_TO_INT64 (m_ulTSRollOver) * CAST_TO_INT64 (MAX_UINT32);
  827. #if 0 // testing sampling frequency estimation for "inaccurate resampling"
  828.     if (pAudioData->uAudioStreamType == TIMED_AUDIO)
  829.     {
  830.         m_startMeasureTime = llActualTimestamp ;
  831.         m_totSamples = 0 ;
  832.     }
  833.     else if (pAudioData->uAudioStreamType == STREAMING_AUDIO)
  834.     {
  835.         float diffTime = (llActualTimestamp - m_startMeasureTime) / 1000.0f ;
  836.         if (diffTime)
  837.         {
  838.             float frEstimate = m_totSamples / m_AudioFmt.uChannels / diffTime ;
  839.             float frEstErr = frEstimate * 0.001f / diffTime ; // 1 ms inaccuracy
  840.             {FILE *f1 = fopen("c:\temp\estimatesr.txt","a+");
  841.              fprintf(f1,"%I64dt%I64dt%ft%fn",llActualTimestamp,llActualEndTime,frEstimate,frEstErr);
  842.              fclose(f1);
  843.             }
  844.         }
  845.     }
  846.     m_totSamples += CalcSamples(pAudioData->pData->GetSize()) ;
  847. #endif
  848.     if ((pAudioData->uAudioStreamType == STREAMING_AUDIO ||
  849.  pAudioData->uAudioStreamType == TIMED_AUDIO) &&
  850.        !(llActualTimestamp >= m_llLastWriteTime ||
  851.  llActualEndTime > m_llLastWriteTime)) 
  852.     {
  853. /* Too late*/
  854. m_bTobeTimed = TRUE;
  855. //{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);}
  856. /*
  857.     {FILE *f1 = fopen("c:\temp\mix.txt","a+"); fprintf(f1,"LATE packetn");
  858.      fclose(f1);
  859.     }
  860.     */  
  861.       
  862.         return HXR_LATE_PACKET;
  863.     }
  864.     pAinfo = new HXAudioInfo;
  865.     if( !pAinfo )
  866.     {
  867.         return HXR_OUTOFMEMORY;
  868.     }
  869.     pAudioData->pData->AddRef();
  870.     pAinfo->pBuffer  = pAudioData->pData;
  871.     pAinfo->ulStartTime   = pAudioData->ulAudioTime;
  872.     pAinfo->pOffset      = pAudioData->pData->GetBuffer();  
  873.     pAinfo->ulBytesLeft = pAudioData->pData->GetSize();  
  874.     pAinfo->uAudioStreamType    = pAudioData->uAudioStreamType;
  875.     if (m_bTobeTimed && pAudioData->uAudioStreamType == STREAMING_AUDIO)
  876.     {
  877. pAinfo->uAudioStreamType = TIMED_AUDIO;
  878. m_bTobeTimed  = FALSE;
  879.     }
  880.     else if (m_bTobeTimed && pAudioData->uAudioStreamType == TIMED_AUDIO)
  881.     {
  882. m_bTobeTimed = FALSE;
  883.     }
  884. //{FILE* f1 = ::fopen("c:\temp\audio.txt", "a+"); ::fprintf(f1, "AddData ulAudioTime: %lun", pAudioData->ulAudioTime);::fclose(f1);}
  885. //////////////////////////////////////////////////////////////////////
  886.                 // XXX wschildbach
  887.     // the start time of this packet in samples. This may be corrected later on.
  888.     pAinfo->llStartTimeInSamples = CAST_TO_INT64(llActualTimestamp) * m_AudioFmt.ulSamplesPerSec / 1000 * m_AudioFmt.uChannels ;
  889.     pAinfo->llEndTimeInSamples   = pAinfo->llStartTimeInSamples + Bytes2Samples(pAinfo->pBuffer->GetSize(), &m_AudioFmt) ;
  890. //////////////////////////////////////////////////////////////////////
  891.     if (pAinfo->uAudioStreamType == INSTANTANEOUS_AUDIO)
  892.     {
  893. CHXSimpleList* pList = new CHXSimpleList;
  894.         if( !pList )
  895.         {
  896.             HX_RELEASE(pAudioData->pData);
  897.             HX_DELETE(pAinfo);
  898.             return HXR_OUTOFMEMORY;
  899.         }
  900. pList->AddHead((void*) pAinfo);
  901. m_pInstantaneousList->AddTail((void*) pList);
  902. m_Owner->m_Owner->ToBeRewound();
  903.     }
  904.     else if (pAinfo->uAudioStreamType == STREAMING_INSTANTANEOUS_AUDIO)
  905.     {
  906. HX_ASSERT(m_pInstantaneousList && m_pInstantaneousList->GetCount() > 0);
  907. CHXSimpleList* pList = NULL;
  908. if (m_pInstantaneousList->GetCount() == 0)
  909. {
  910.     pList = new CHXSimpleList;
  911.             if( !pList )
  912.             {
  913.                 HX_RELEASE(pAudioData->pData);
  914.                 HX_DELETE(pAinfo);
  915.                 return HXR_OUTOFMEMORY;
  916.             }
  917.     m_pInstantaneousList->AddTail((void*) pList);
  918.     // fix for naive users!
  919.     pAinfo->uAudioStreamType = INSTANTANEOUS_AUDIO;
  920.     m_Owner->m_Owner->ToBeRewound();
  921. }
  922. pList = (CHXSimpleList*) m_pInstantaneousList->GetTail();
  923. pList->AddTail(pAinfo);
  924.     }
  925.     else if (m_pDataList->IsEmpty())
  926.     {
  927. /*
  928.     {FILE *f1 = fopen("c:\temp\mix.txt","a+"); fprintf(f1,"adding to empty list...n");
  929.      fclose(f1);
  930.     }
  931. */
  932.         m_pDataList->AddTail((void*) pAinfo);
  933.     }
  934.     else
  935.     {
  936. /*
  937.     {FILE *f1 = fopen("c:\temp\mix.txt","a+"); fprintf(f1,"adding to non-empty list...n");
  938.      fclose(f1);
  939.     }
  940. */
  941. HXAudioInfo* pInfo = (HXAudioInfo*) m_pDataList->GetTail();
  942. UINT32 ulActualTSRollOver = m_ulTSRollOver;
  943. if (bInTSRollOver && ulActualTSRollOver)
  944. {
  945.     ulActualTSRollOver--;
  946. }
  947. INT64 llActualLastEndTime = CAST_TO_INT64 (pInfo->ulStartTime) + CAST_TO_INT64 (CalcMs(pInfo->pBuffer->GetSize())) +
  948.     CAST_TO_INT64 ulActualTSRollOver * CAST_TO_INT64 MAX_UINT32;
  949. INT64 llActualLastStartTime = CAST_TO_INT64 (pInfo->ulStartTime) + CAST_TO_INT64 ulActualTSRollOver * CAST_TO_INT64 MAX_UINT32;
  950. if (llActualTimestamp < llActualLastStartTime)
  951. {
  952.     /* Not allowed */
  953.     theErr = HXR_OUTOFORDER_PACKET; 
  954.     /* something is fu*#$#up... figure out what?*/
  955.     HX_ASSERT(!("Packets written out of order"));
  956.     goto exit;
  957. }
  958. if (pAinfo->uAudioStreamType == STREAMING_AUDIO)
  959. {
  960.     /* is it a resonable packet to add to the list */
  961.     if ((llActualTimestamp <= llActualLastEndTime &&
  962.  llActualLastEndTime - llActualTimestamp <= m_ulFudge) || 
  963. (llActualTimestamp >= llActualLastEndTime &&
  964.  llActualTimestamp - llActualLastEndTime <= m_ulFudge))
  965.     {
  966.                 // XXX wschildbach
  967.                 // make 64-bit timestamps contiguous before adding into the queue.
  968.                 pAinfo->llEndTimeInSamples   += pInfo->llEndTimeInSamples - pAinfo->llStartTimeInSamples ;
  969.                 pAinfo->llStartTimeInSamples  = pInfo->llEndTimeInSamples ;
  970.                 m_pDataList->AddTail((void*) pAinfo);
  971.                 
  972.     }
  973.     else
  974.     {
  975. theErr = HXR_NONCONTIGUOUS_PACKET; //HX_LATE_PACKET;
  976. /* something is fu*#$#up... figure out what?*/
  977. HX_ASSERT(!("Streaming Audio: Non-Contigous Write"));
  978. m_bTobeTimed = TRUE;
  979. goto exit;
  980.     }
  981. }
  982. else
  983. {
  984.     /* see if there is any overlap.. we do not allow any overlap */
  985.     if (llActualTimestamp < llActualLastEndTime &&
  986. llActualLastEndTime - llActualTimestamp > m_ulFudge)
  987.     {
  988. /* hmmm an overlapped packet */
  989. theErr = HXR_OVERLAPPED_PACKET;
  990. /* something is fu*#$#up... figure out what?*/
  991. HX_ASSERT(!("Timed Audio: Overlapping write"));
  992. m_bTobeTimed = TRUE;
  993. goto exit;
  994.     }
  995.     else
  996.     {
  997. m_pDataList->AddTail((void*) pAinfo);
  998.     }
  999. }
  1000.     }
  1001. exit:
  1002.     if (theErr != HXR_OK && pAinfo)
  1003.     {
  1004. pAinfo->pBuffer->Release();
  1005. delete pAinfo;
  1006.     }
  1007. //{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);}
  1008.     return theErr;
  1009. }
  1010. HX_RESULT CHXAudioStream::ProcessInfo(void)
  1011. {
  1012.     HX_RESULT theErr = HXR_OK;
  1013.     m_ulSampleFrameSize = m_AudioFmt.uChannels * (m_AudioFmt.uBitsPerSample>>3) ;
  1014.     m_ulPendingAudioBytes = 0 ;
  1015.     // Calculate the number of bytes per granularity.
  1016.     // XXX wschildbach: These formulas are suspect. There is no feedback to the player
  1017.     // or audio session that this is what we assume for a size. I believe this is either
  1018.     // not needed or too complex.
  1019.     m_ulInputBytesPerGran = (ULONG32) 
  1020. (((m_AudioFmt.uChannels * (m_AudioFmt.uBitsPerSample>>3) * m_AudioFmt.ulSamplesPerSec) 
  1021. / 1000.0) * m_ulGranularity);
  1022.     m_ulOutputBytesPerGran  = (ULONG32) 
  1023. (((m_DeviceFmt.uChannels * (m_DeviceFmt.uBitsPerSample>>3) * m_DeviceFmt.ulSamplesPerSec) 
  1024. / 1000.0) * m_ulGranularity);
  1025.     // Make sure that number of bytes per granularity is an even number.
  1026.     m_ulInputBytesPerGran  -= m_ulInputBytesPerGran  % ((m_AudioFmt.uBitsPerSample>>3)*m_AudioFmt.uChannels);
  1027.     m_ulOutputBytesPerGran -= m_ulOutputBytesPerGran % ((m_DeviceFmt.uBitsPerSample>>3)*m_DeviceFmt.uChannels);
  1028.     if (!theErr)
  1029.     {
  1030.         // set up the mixing engine
  1031.         // XXX wschildbach
  1032.         theErr = m_pMixEngine->Init(m_AudioFmt.ulSamplesPerSec, m_DeviceFmt.ulSamplesPerSec, m_AudioFmt.uChannels, m_DeviceFmt.uChannels) ;
  1033.         if (SUCCEEDED(theErr))
  1034.             theErr = m_pMixEngine->SetSampleConverter(this) ;
  1035.         if (SUCCEEDED(theErr))
  1036.             theErr = m_pMixEngine->SetOutputBytesPerSample(m_DeviceFmt.uBitsPerSample / 8) ;
  1037.         // set the volume (somebody might have set it when we did not have an engine)
  1038. #ifdef HELIX_FEATURE_GAINTOOL
  1039.         if (SUCCEEDED(theErr))
  1040.             m_pMixEngine->SetVolume(m_pMixEngine->HXVolume2TenthOfDB(m_bMute ? HX_MIN_VOLUME : m_uVolume)) ;
  1041. #endif
  1042.     }
  1043.     if (!theErr)
  1044.     {
  1045. m_bInited = TRUE;
  1046. if (m_eState == E_STOPPED)
  1047. {
  1048.     m_eState = E_INITIALIZED;
  1049.     }
  1050.     /* Get the current player time to set the last write audio time 
  1051.      * If someone creates a stream mid presentation, we ask the player 
  1052.      * object for the current write time.
  1053.      */
  1054.     // set last write time to be the current playback time since
  1055.     // this is what other system components(i.e. renderers) based on 
  1056.     // fixed b#69847 - loss of push-down-worth of data =
  1057.     // m_Owner->GetLastAudioWriteTime() - m_Owner->GetCurrentPlayBackTime()
  1058. //    m_llLastWriteTime = m_Owner->GetCurrentPlayBackTime();
  1059.     // XXXRA: It is necessary to use last audio write time for any delayed
  1060.     // audio streams to work that do not involve any Pause/Rewind logic.
  1061.     // To cover the case where a source (and an audio stream) has been added
  1062.     // mid-playback by SMIL renderer which has a delay equivalent to the 
  1063.     // current playback time, it should result in a player rewind which should 
  1064.     // reset the lastaudiowrite time accordingly...so we should be able
  1065.     // to use m_Owner->GetLastAudioWriteTime() value in such a use case as well.
  1066.     // this change is required to fix PR 79161 and PR 69780.
  1067.     // Henry, PR 69847 (the reason for the earlier change) is still busted. 
  1068.     // so I am reverting this code to the original code. you will have 
  1069.     // to come up with a different fix for PR 69847 since this was clearly not 
  1070.     // the correct fix.
  1071.     m_llLastWriteTime = m_Owner->GetLastAudioWriteTime();
  1072.     m_pMixEngine->ResetTimeLineInMillis(m_llLastWriteTime) ;
  1073.     if (!theErr && m_bInited)
  1074.     {
  1075. m_Owner->StreamInitialized(this);
  1076.     }
  1077.     return theErr;
  1078. }
  1079. /************************************************************************
  1080.  *  Method:
  1081.  * IHXAudioStream::GetFormat
  1082.  * Purpose:
  1083.  *   Return the stream's audio format.
  1084.  */
  1085. HX_RESULT CHXAudioStream::GetFormat
  1086.     HXAudioFormat* pAudioFormat
  1087. )
  1088. {
  1089.     if (!m_bAudioFormatKnown)
  1090.     {
  1091. return HXR_NOT_INITIALIZED;
  1092.     }
  1093.     pAudioFormat->uChannels       = m_AudioFmt.uChannels;
  1094.     pAudioFormat->uBitsPerSample  = m_AudioFmt.uBitsPerSample;
  1095.     pAudioFormat->ulSamplesPerSec = m_AudioFmt.ulSamplesPerSec;
  1096.     pAudioFormat->uMaxBlockSize   = m_AudioFmt.uMaxBlockSize;
  1097.     return HXR_OK;
  1098. }
  1099. /************************************************************************
  1100.  *  Method:
  1101.  * IHXAudioStream::Setup
  1102.  * Purpose:
  1103.  * This is called by the player's Setup method. At this
  1104.  * time the audio device format is set and we can now
  1105.  * set up the streams pre-mixing buffer. This buffer
  1106.  * contains data that has been resampled to match the
  1107.  * audio device format.
  1108.  */
  1109. HX_RESULT CHXAudioStream::Setup(
  1110. HXAudioFormat* pFormat
  1111. , ULONG32 ulGranularity
  1112. )
  1113. {
  1114.     HX_RESULT theErr = HXR_OK;
  1115.     memcpy( &m_DeviceFmt, pFormat, sizeof(HXAudioFormat) );
  1116.     m_ulGranularity = ulGranularity;
  1117.     m_bSetupDone = TRUE;
  1118.     
  1119.     /* we have all the info now.. so setup the resampler */
  1120.     if (m_bAudioFormatKnown && !m_bInited)
  1121.     {
  1122. theErr = ProcessInfo();
  1123.     }
  1124.     return theErr;
  1125. }
  1126. /************************************************************************
  1127.  *  Method:
  1128.  * IHXAudioStream::ResetStream
  1129.  * Purpose:
  1130.  */
  1131. void CHXAudioStream::ResetStream()
  1132. {
  1133.     m_bInited = FALSE;
  1134.     m_bCanBeRewound = FALSE;
  1135.     m_bSetupDone = FALSE;
  1136.     m_bAudioFormatKnown = FALSE;
  1137.     m_bIsResumed = FALSE;
  1138.     
  1139.     UnRegister();
  1140.     while (m_pAvailableBuffers && m_pAvailableBuffers->GetCount() > 0)
  1141.     {
  1142. IHXBuffer* pBuffer = (IHXBuffer*) m_pAvailableBuffers->RemoveHead();
  1143. HX_RELEASE(pBuffer);
  1144.     }
  1145.     HX_DELETE(m_pAvailableBuffers);
  1146.     // Delete all entries in the audio data list
  1147.     FlushBuffers();
  1148.     HX_DELETE(m_pDataList);
  1149.     HX_DELETE(m_pInstantaneousList);
  1150.     CleanupRAByToTs();
  1151.     HX_DELETE(m_pRAByToTsInList);
  1152.     HX_DELETE(m_pRAByToTsAdjustedList);
  1153.     HX_DELETE(m_pMixEngine);
  1154.     m_bGotHooks = FALSE;
  1155.     m_llLastWriteTime = 0;
  1156.     m_ulTSRollOver = 0;
  1157.     HX_RELEASE(m_pValues);
  1158. #if defined(HELIX_FEATURE_AUDIO_PREMIXHOOK)
  1159.     // Delete all entries in the pre-mix hook list.
  1160.     if ( m_PreMixHookMap.GetCount() > 0)
  1161.     {
  1162.         HXAudioHookInfo* h = 0;
  1163.         CHXMapPtrToPtr::Iterator lIter = m_PreMixHookMap.Begin();
  1164.         for (; lIter != m_PreMixHookMap.End(); ++lIter)
  1165.         {
  1166.             h = (HXAudioHookInfo*) (*lIter);
  1167.     ProcessAudioHook(ACTION_REMOVE, h->pHook);
  1168.             h->pHook->Release();
  1169.             delete h;
  1170.         }
  1171. m_PreMixHookMap.RemoveAll();
  1172.     }
  1173. #endif /* HELIX_FEATURE_AUDIO_PREMIXHOOK */
  1174. #ifdef HELIX_FEATURE_VOLUME
  1175.     if( m_pStreamVolume )
  1176.     {
  1177.         m_pStreamVolume->RemoveAdviseSink(this);
  1178.         m_pStreamVolume->Release();
  1179.         m_pStreamVolume=NULL;
  1180.     }
  1181. #endif    
  1182.     
  1183.     HX_DELETE(m_pInDataPtr);
  1184.     HX_DELETE(m_pOutDataPtr);
  1185.     if (m_DryNotificationMap && m_DryNotificationMap->GetCount() > 0)
  1186.     {
  1187.         IHXDryNotification* pDryNotification = 0;
  1188.         CHXMapPtrToPtr::Iterator lIter = m_DryNotificationMap->Begin();
  1189.         for (; lIter != m_DryNotificationMap->End(); ++lIter)
  1190.         {
  1191.             pDryNotification = (IHXDryNotification*) (*lIter);
  1192.             pDryNotification->Release();
  1193.         }
  1194. m_DryNotificationMap->RemoveAll();
  1195.     }
  1196.     HX_RELEASE(m_pCrossFadeStream);
  1197.     HX_RELEASE(m_pCommonClassFactory);
  1198. #if defined(HELIX_FEATURE_PREFERENCES)
  1199.     HX_RELEASE(m_pPreferences);
  1200. #endif /* HELIX_FEATURE_PREFERENCES */
  1201.     HX_RELEASE(m_Owner);
  1202.     return;
  1203. }
  1204. HX_RESULT
  1205. CHXAudioStream::ProcessAudioHook(PROCESS_ACTION action, 
  1206.  IHXAudioHook* pAudioHook)
  1207. {
  1208.     return HXR_OK;
  1209. }
  1210. /************************************************************************
  1211.  *  Method:
  1212.  * IHXAudioStream::InitHooks
  1213.  * Purpose:
  1214.  * Init any pre-mix hooks. Return TRUE if hooks exist else return
  1215.  * FALSE.
  1216.  */
  1217. void CHXAudioStream::InitHooks()
  1218. {
  1219. #if defined(HELIX_FEATURE_AUDIO_PREMIXHOOK)
  1220.     /* Iterate thru the hook list and call the hook's OnInit().
  1221.      * If any of the hooks have disabled write set to TRUE, then
  1222.      * we will let this override any set to FALSE.
  1223.      */
  1224.     if ( m_PreMixHookMap.GetCount() > 0 )
  1225.     {
  1226. HXAudioHookInfo* h = 0;
  1227. CHXMapPtrToPtr::Iterator lIter = m_PreMixHookMap.Begin();
  1228. for (; lIter != m_PreMixHookMap.End(); ++lIter)
  1229. {
  1230.     h = (HXAudioHookInfo*) (*lIter);
  1231.     if (h->bIgnoreAudioData ||
  1232. HXR_OK == ProcessAudioHook(ACTION_CHECK, h->pHook))
  1233.     {
  1234. h->pHook->OnInit( &m_AudioFmt );
  1235.     }
  1236. }
  1237.     }
  1238. #endif /* HELIX_FEATURE_AUDIO_PREMIXHOOK */
  1239.     m_bHooksInitialized = TRUE;
  1240. }
  1241. /************************************************************************
  1242.  *  Method:
  1243.  * IHXAudioStream::ProcessHooks
  1244.  * Purpose:
  1245.  */
  1246. HX_RESULT CHXAudioStream::ProcessHooks
  1247.     HXAudioData* pInData,
  1248.     HXAudioData* pOutData
  1249. )
  1250. {
  1251.     HX_RESULT theErr = HXR_OK;
  1252. #if defined(HELIX_FEATURE_AUDIO_PREMIXHOOK)
  1253.     m_pInDataPtr->pData = pInData->pData;
  1254.     m_pInDataPtr->pData->AddRef();
  1255.     m_pInDataPtr->ulAudioTime = pInData->ulAudioTime;
  1256.     
  1257.     m_pOutDataPtr->pData = NULL;
  1258.     m_pOutDataPtr->ulAudioTime = pInData->ulAudioTime;
  1259.     m_pInDataPtr->uAudioStreamType    = pInData->uAudioStreamType;
  1260.     m_pOutDataPtr->uAudioStreamType   = pInData->uAudioStreamType;
  1261.     if ( m_PreMixHookMap.GetCount() > 0 )
  1262.     {
  1263. HXAudioHookInfo* pPreMixHookInfo = 0;
  1264. CHXMapPtrToPtr::Iterator lIter = m_PreMixHookMap.Begin();
  1265. for (; !theErr && lIter != m_PreMixHookMap.End(); ++lIter)
  1266. {
  1267.     pPreMixHookInfo = (HXAudioHookInfo*) (*lIter);
  1268.     if (HXR_OK == ProcessAudioHook(ACTION_CHECK, pPreMixHookInfo->pHook))
  1269.     {
  1270.                 // XXXHP, disable hooks when it doesn't support multi-channel
  1271.                 if (m_AudioFmt.uChannels <= 2 || pPreMixHookInfo->bMultiChannelSupport)
  1272.                 {
  1273.     theErr = pPreMixHookInfo->pHook->OnBuffer( m_pInDataPtr, m_pOutDataPtr);
  1274.     /* Check to see if renderer changed the buffer. If so, then
  1275.      * make this output as input to the next Hook.
  1276.      */
  1277.     if (!theErr && m_pOutDataPtr->pData)
  1278.     {
  1279.         m_pInDataPtr->pData->Release();
  1280.         m_pInDataPtr->pData     = m_pOutDataPtr->pData;
  1281.         m_pInDataPtr->ulAudioTime   = m_pOutDataPtr->ulAudioTime;
  1282.         m_pOutDataPtr->pData = 0;
  1283.     }
  1284.                 }
  1285.     }
  1286.     else if (pPreMixHookInfo->bIgnoreAudioData)
  1287.     {
  1288. IHXBuffer* pTempBuf = m_pInDataPtr->pData;
  1289. m_pInDataPtr->pData = NULL;
  1290. theErr = pPreMixHookInfo->pHook->OnBuffer( m_pInDataPtr, m_pOutDataPtr);
  1291. m_pInDataPtr->pData = pTempBuf;
  1292.     }
  1293. }
  1294.     }
  1295.     /* Final output is always in InDataPtr*/
  1296.     pOutData->pData = m_pInDataPtr->pData;
  1297.     pOutData->ulAudioTime = m_pInDataPtr->ulAudioTime;
  1298.     pOutData->uAudioStreamType  = m_pInDataPtr->uAudioStreamType;
  1299. #endif /* HELIX_FEATURE_AUDIO_PREMIXHOOK */
  1300.     return theErr;
  1301. }
  1302. /************************************************************************
  1303.  *  Method:
  1304.  * CHXAudioStream::MixIntoBuffer
  1305.  *  Purpose:
  1306.  * Mix stream data into this pPlayerBuf.
  1307.  *
  1308.  */
  1309. HX_RESULT CHXAudioStream::MixIntoBuffer
  1310. (
  1311.     UCHAR*   pPlayerBuf,
  1312.     ULONG32  ulBufSize,
  1313.     ULONG32& ulBufTime,
  1314.     BOOL&    bIsMixBufferDirty,
  1315.     BOOL     bGetCrossFadeData
  1316. )
  1317. {
  1318.     HX_RESULT res = HXR_OK ;
  1319.     if (!m_bInited)
  1320.     {
  1321. return HXR_NOT_INITIALIZED;
  1322.     }
  1323.     // bGetCrossFadeData should now be a thing of the past.
  1324.     HX_ASSERT(!bGetCrossFadeData) ;
  1325. //{FILE* f1 = ::fopen("c:\temp\rasync.txt", "a+"); ::fprintf(f1, "Call MixIntoBuffer: %lun", m_ulLastWriteTime);::fclose(f1);}
  1326.     /* If this is a *FROM* stream, we may have already mixed
  1327.      * data during cross-fade with *TO* stream
  1328.      */
  1329.     // update the outside world's sense of time.
  1330.     // XXX wschildbach: how to account for rollover?
  1331.     INT64 llNextMixTime = m_pMixEngine->GetNextMixTimeMillis();
  1332.     UINT32 ulLastWriteTime = INT64_TO_UINT32(llNextMixTime - CAST_TO_INT64(m_ulTSRollOver) * CAST_TO_INT64(MAX_UINT32));
  1333.     // In a surestream situation, ulBufTime is 0 on the first stream, and a time corresponding
  1334.     // to the granularity in the second stream. I don't know what this code tries to do.
  1335.     // XXX wschildbach
  1336.     if (ulBufTime < ulLastWriteTime)
  1337.     {
  1338. ulBufTime = ulLastWriteTime;
  1339.     }
  1340.     /* If there are any DryNotifications and the data list is empty
  1341.      * we need to notify them so that they can write more data.
  1342.      *
  1343.      * If EnoughDataAvailable() returns FALSE, it will have updated
  1344.      * the input values to point at where the buffer needs to be filled.
  1345.      */
  1346.     INT64  llStartMix, llEndMix;
  1347.     m_pMixEngine->GetMixRange(ulBufSize, llStartMix, llEndMix) ;
  1348.     UINT32 nSamplesNeeded = INT64_TO_UINT32(llEndMix - llStartMix) ; // always fits into UINT32
  1349.     if (!EnoughDataAvailable(llStartMix, nSamplesNeeded))
  1350.     {
  1351.         // Check if the audio device is really empty ("ReallyNeedData") or if we are
  1352.         // just over-eagerly filling up the pre-roll. If the latter is the case,
  1353.         // return HXR_WOULD_BLOCK
  1354. if (!bIsMixBufferDirty && !m_Owner->m_Owner->ReallyNeedData())
  1355. {
  1356.     return HXR_WOULD_BLOCK;
  1357. }
  1358.         // renderer might pause playback if has no packets
  1359. if (m_DryNotificationMap->GetCount() > 0)
  1360. {
  1361.     IHXDryNotification* pDryNotification = 0;
  1362.     CHXMapPtrToPtr::Iterator lIter = m_DryNotificationMap->Begin();
  1363.     for (; lIter != m_DryNotificationMap->End(); ++lIter)
  1364.     {
  1365. pDryNotification = (IHXDryNotification*) (*lIter);
  1366.                 UINT64 streamTime = Samples2Ms(llStartMix, &m_AudioFmt) -
  1367.                     CAST_TO_INT64(m_ulTSRollOver)*CAST_TO_INT64(MAX_UINT32);
  1368. HX_RESULT theErr = pDryNotification->OnDryNotification( INT64_TO_UINT32(streamTime),
  1369.                                                      INT64_TO_UINT32(Samples2Ms(nSamplesNeeded, &m_AudioFmt))) ;
  1370.                 if( theErr == HXR_OUTOFMEMORY )
  1371.                 {
  1372.                     return theErr;
  1373.                 }
  1374.     }
  1375.     if (m_Owner->GetState() != E_PLAYING)
  1376.     {
  1377. return HXR_OK;
  1378.     }
  1379. }
  1380.     }
  1381.     // XXX wschildbach what does this do?
  1382.     m_Owner->DataInAudioDevice(TRUE);
  1383.     /*
  1384.     {FILE *f1 = fopen("c:\temp\mix.txt","a+");
  1385.      fprintf(f1,"ncall MixIntoBuffer(%I64d, id=%ld)n",(INT64)mixTimeInSamples,m_pMixEngine - (HXAudioSvcMixEngine*)0xdde198);
  1386.      fclose(f1);}
  1387. */
  1388.     // this call does all the mixing.
  1389.     res = m_pMixEngine->MixIntoBuffer(pPlayerBuf, ulBufSize, bIsMixBufferDirty) ;
  1390.     if( m_wLastError == HXR_OUTOFMEMORY )
  1391.     {
  1392.         return m_wLastError;
  1393.     }
  1394.  
  1395.     if (FAILED(res))
  1396.         return res ; 
  1397. #if defined(HELIX_FEATURE_AUDIO_INACCURATESAMPLING)
  1398.     if( m_bRealAudioStream )
  1399.         MapFudgedTimestamps();
  1400. #endif    
  1401.     
  1402.     // update the inside world's sense of time.
  1403.     m_llLastWriteTime = m_pMixEngine->GetNextMixTimeMillis() ;
  1404. #if 0
  1405.     {FILE *f = fopen("c:\temp\incoming.txt","a+");
  1406.     fprintf(f,"mix %ld %I64dn",ulBufTime,m_llLastWriteTime);fclose(f);}
  1407. #endif
  1408.     return HXR_OK;
  1409. }
  1410. /*
  1411.  * This is the callback function that m_pMixEngine->MixIntoBuffer() will call to read
  1412.  * new samples.
  1413.  */
  1414. BOOL CHXAudioStream::ConvertIntoBuffer(tAudioSample* buffer, UINT32 nSamples, INT64 llStartTimeInSamples)
  1415. {
  1416.     HXAudioInfo*    pInfo           = 0;
  1417.     LISTPOSITION    lp              = 0;
  1418.     INT32           nBytesPerSample = m_AudioFmt.uBitsPerSample>>3 ;
  1419.     BOOL            didMix          = FALSE ; // set to TRUE if we have packets in this time range
  1420.     BOOL            bPacketsAfterRange = FALSE;
  1421.     /*
  1422.     {FILE *f1 = fopen("c:\temp\mix.txt","a+");
  1423.      fprintf(f1," ConvertIntoBuffer(%I64d - %I64d, len=%ld)n",(INT64)llStartTimeInSamples,(INT64)llStartTimeInSamples+nSamples,nSamples);
  1424.      fclose(f1);}
  1425.      */
  1426.     // there are two lists of packets here: timed audio and instantaneous audio.
  1427.     // We only look into the list for timed buffers -- Instantaneoue audio is ignored
  1428.     // (it never properly worked anyway, so support is discontinued).
  1429.     /* remove old packets. Old packets are packets that have an end time that is before
  1430.        our current mix time. */
  1431.     lp = m_pDataList->GetHeadPosition();
  1432.     while( lp )
  1433.     {
  1434. LISTPOSITION lastlp = lp;
  1435. pInfo = (HXAudioInfo*) m_pDataList->GetNext(lp);
  1436. if (pInfo->llEndTimeInSamples < llStartTimeInSamples)
  1437. {
  1438.             /*
  1439.     {FILE *f1 = fopen("c:\temp\mix.txt","a+");
  1440.      fprintf(f1,"-- reaping packet (%I64d,%I64d)n",
  1441.          pInfo->llStartTimeInSamples,pInfo->llEndTimeInSamples);
  1442.      fclose(f1);}
  1443.      */
  1444.     FreeInfo(pInfo);
  1445.     m_pDataList->RemoveAt(lastlp);
  1446.             if( m_wLastError == HXR_OUTOFMEMORY )
  1447.             {
  1448.                 return FALSE;
  1449.             }
  1450. }
  1451.         else // if monotonous and non-overlapping
  1452.             break ;
  1453.     }
  1454.     // now go through the entire list of packets, and look for overlap with the
  1455.     // convert buffer. Any packet with overlap will be at least partially converted
  1456.     // into the buffer.
  1457.     // If packets overlap, one packet will then take precedence over another -- not
  1458.     // much we can do about that.
  1459.     lp = m_pDataList->GetHeadPosition();
  1460.     while( lp )
  1461.     {
  1462. pInfo = (HXAudioInfo*) m_pDataList->GetNext(lp);
  1463. if (pInfo->llStartTimeInSamples < llStartTimeInSamples + nSamples &&
  1464.             pInfo->llEndTimeInSamples > llStartTimeInSamples)
  1465.         {
  1466.             // This packet has some overlap with what we are converting.
  1467.             // if this is the first packet to be mixed into this buffer,
  1468.             // silence out the entire buffer. This is inefficient, but safe
  1469.             if (!didMix)
  1470.                 CAudioSvcSampleConverter::silence(buffer, nSamples) ;
  1471.             didMix = TRUE ;
  1472.             INT32 nMixbufferOffset = 0;
  1473.             INT32 pastPacketStart = INT64_TO_INT32(llStartTimeInSamples - pInfo->llStartTimeInSamples) ;
  1474.             if (pastPacketStart < 0)
  1475.             {
  1476.                 nMixbufferOffset = -pastPacketStart;
  1477.                 pastPacketStart = 0 ;
  1478.             }
  1479.             INT32 nn = Bytes2Samples(pInfo->pBuffer->GetSize(), &m_AudioFmt) ;
  1480.             INT32 nSamplesToUse = nn - pastPacketStart;
  1481.             if (nSamplesToUse > (INT32)(nSamples - nMixbufferOffset))
  1482.                 nSamplesToUse = nSamples - nMixbufferOffset;
  1483.             const unsigned char *cvtin = pInfo->pBuffer->GetBuffer() + pastPacketStart * nBytesPerSample ;
  1484.             tAudioSample *cvtout = buffer + nMixbufferOffset ;
  1485.             /*
  1486.     {FILE *f1 = fopen("c:\temp\mix.txt","a+");
  1487.      fprintf(f1,"  mix packet (%I64d,%I64d,len=%ld) into buffer(%ld,%ld,len=%ld)n",
  1488.          pInfo->llStartTimeInSamples,pInfo->llEndTimeInSamples,
  1489.          nn,
  1490.          nMixbufferOffset,nMixbufferOffset+nSamplesToUse,nSamplesToUse);
  1491.      fprintf(f1,"  pastPacketStart = %I64d, nMixbufferOffset = %ldn",
  1492.                 pastPacketStart,nMixbufferOffset) ;
  1493.      fclose(f1);}
  1494. */
  1495.             switch (nBytesPerSample)
  1496.             {
  1497.             case 1:
  1498.                 CAudioSvcSampleConverter::cvt8(cvtin, cvtout, nSamplesToUse);
  1499.                 break ;
  1500.             case 2:
  1501.                 CAudioSvcSampleConverter::cvt16(cvtin, cvtout, nSamplesToUse);
  1502.                 break ;
  1503.             case 4:
  1504.                 CAudioSvcSampleConverter::cvt32(cvtin, cvtout, nSamplesToUse);
  1505.                 break ;
  1506.             }
  1507.         }
  1508. else if (pInfo->llStartTimeInSamples >= llStartTimeInSamples + nSamples)
  1509. {
  1510.     /* We've found audio data that is past the
  1511.      * desired range.
  1512.      */
  1513.     bPacketsAfterRange = TRUE;
  1514. }
  1515.     }
  1516.     if (!didMix && bPacketsAfterRange)
  1517.     {
  1518. /* We do not have packets for this range, but we
  1519.  * do have packets after the range.
  1520.  * Create silence data and make it look like we
  1521.  * actually had data for this range.
  1522.  */
  1523. CAudioSvcSampleConverter::silence(buffer, nSamples) ;
  1524. didMix = TRUE;
  1525.     }
  1526.     return didMix ;
  1527. }
  1528. /************************************************************************
  1529.  *  Method:
  1530.  *              CHXAudioStream::Bytes2Samples
  1531.  *      Purpose:
  1532.  * Translate from units of bytes to samples.
  1533.  */
  1534. UINT32 CHXAudioStream::Bytes2Samples 
  1535. (
  1536.     UINT64 ulNumBytes,
  1537.     const HXAudioFormat *fmt 
  1538. )
  1539. {
  1540.     ASSERT(ulNumBytes % (fmt->uBitsPerSample >> 3) == 0) ;
  1541.     return INT64_TO_UINT32(ulNumBytes / (fmt->uBitsPerSample >> 3)) ;
  1542. }
  1543. /************************************************************************
  1544.  *  Method:
  1545.  *              CHXAudioStream::Samples2Ms
  1546.  *      Purpose:
  1547.  * Calculate the duration in millisecs for this number of samples.
  1548.  */
  1549. UINT64 CHXAudioStream::Samples2Ms
  1550. (
  1551.     INT64 nSamples,
  1552.     const HXAudioFormat *fmt
  1553. )
  1554. {
  1555.     UINT32 ulDenom = fmt->uChannels * fmt->ulSamplesPerSec;
  1556.     UINT64 q = nSamples / ulDenom;
  1557.     UINT64 r = nSamples - q * ulDenom;
  1558.     return q * 1000 + (r * 1000) / ulDenom;
  1559. }
  1560. /************************************************************************
  1561.  *  Method:
  1562.  *              CHXAudioStream::CalcMs
  1563.  *      Purpose:
  1564.  * Calculate the duration in millisecs for this number of
  1565.  *              bytes in input format.
  1566.  */
  1567. ULONG32 CHXAudioStream::CalcMs
  1568. (
  1569.     ULONG32 ulNumBytes
  1570. )
  1571. {
  1572.     return INT64_TO_ULONG32(Samples2Ms(Bytes2Samples(ulNumBytes, &m_AudioFmt), &m_AudioFmt));
  1573. }
  1574. /************************************************************************
  1575.  *  Method:
  1576.  *              CHXAudioStream::CalcDeviceMs
  1577.  *      Purpose:
  1578.  * Calculate the duration in millisecs for this number of 
  1579.  * bytes in Device format.
  1580.  */
  1581. ULONG32 CHXAudioStream::CalcDeviceMs
  1582. (
  1583.     ULONG32 ulNumBytes
  1584. )
  1585. {
  1586.     return INT64_TO_ULONG32(Samples2Ms(Bytes2Samples(ulNumBytes, &m_DeviceFmt), &m_DeviceFmt));
  1587. }
  1588. /************************************************************************
  1589.  *  Method:
  1590.  *              CHXAudioStream::CalcOffset
  1591.  *      Purpose:
  1592.  * Calculate the offset in bytes given time.
  1593.  */
  1594. UINT32 CHXAudioStream::CalcOffset
  1595. (
  1596.     INT64 llStartTime
  1597. ,   INT64 llEndTime
  1598. )
  1599. {
  1600.     /* Using m_ulBytesPerMs may introduce cumulative error due 
  1601.      * to decimal cutoff 
  1602.      */
  1603.     HX_ASSERT(llEndTime - llStartTime < MAX_TIMESTAMP_GAP);
  1604.     return m_ulGranularity ?
  1605.         INT64_TO_UINT32((llEndTime - llStartTime) * m_ulInputBytesPerGran / m_ulGranularity) :
  1606.         0 ;
  1607. }
  1608. void CHXAudioStream::FlushBuffers(BOOL bInstantaneousAlso)
  1609. {
  1610.     while (m_pDataList && m_pDataList->GetCount() > 0)
  1611.     {
  1612. HXAudioInfo* pInfo = (HXAudioInfo*) m_pDataList->RemoveHead();
  1613. FreeInfo(pInfo);
  1614.     }
  1615.     while (bInstantaneousAlso && m_pInstantaneousList && m_pInstantaneousList->GetCount() > 0)
  1616.     {
  1617. CHXSimpleList* pList = (CHXSimpleList*) m_pInstantaneousList->RemoveHead();
  1618. while (pList->GetCount() > 0)
  1619. {
  1620.     HXAudioInfo* pInfo = (HXAudioInfo*) pList->RemoveHead();
  1621.     FreeInfo(pInfo, TRUE);
  1622. }
  1623. HX_DELETE(pList);
  1624.     }
  1625.     // reset m_bLastNMilliSecsToBeSaved so that we actually 
  1626.     // delete buffers in FreeInfo
  1627.     BOOL bLastNMilliSecsToBeSaved = m_bLastNMilliSecsToBeSaved;
  1628.     m_bLastNMilliSecsToBeSaved = FALSE;
  1629.     while (m_pLastNMilliSecsList && m_pLastNMilliSecsList->GetCount() > 0)
  1630.     {
  1631. HXAudioInfo* pInfo = (HXAudioInfo*) m_pLastNMilliSecsList->RemoveHead();
  1632. FreeInfo(pInfo);
  1633.     }
  1634.     m_bLastNMilliSecsToBeSaved = bLastNMilliSecsToBeSaved;
  1635.     HX_DELETE(m_pLastNMilliSecsList);
  1636. }
  1637. /*
  1638.     this routine checks if there are enough packets waiting in the queue
  1639.     to be mixed. It will return FALSE if not, or if there are packets missing
  1640.     in the middle of the queue.
  1641. */
  1642. BOOL
  1643. CHXAudioStream::EnoughDataAvailable(INT64& llStartTimeInSamples, UINT32& nSamplesRequired)
  1644. {
  1645.     INT64           llEndTimeInSamples   = llStartTimeInSamples + nSamplesRequired ;
  1646.     HXAudioInfo*    pInfoOld        = 0 ;
  1647.     HXAudioInfo*    pInfo           = 0;
  1648.     LISTPOSITION    lp              = 0;
  1649.     // if the list is completely empty, report the whole data range as missing
  1650.     if (m_pDataList->IsEmpty())
  1651. return FALSE ;
  1652.     nSamplesRequired = 0 ;
  1653.     /* skip over old packets. Old packets are packets that have an end time that is before
  1654.        our current mix time. */
  1655.     lp = m_pDataList->GetHeadPosition();
  1656.     while( lp )
  1657.     {
  1658. pInfoOld = (HXAudioInfo*) m_pDataList->GetNext(lp);
  1659. if (pInfoOld->llEndTimeInSamples >= llStartTimeInSamples)
  1660.             break ;
  1661.     }
  1662. #if 0 // disabled missing packet detection
  1663.     // pInfoOld is the first packet to be mixed. To make sure it overlaps with the start
  1664.     // of the mix buffer, do this (disabled for now):
  1665.     
  1666.     if (pInfoOld->llStartTimeInSamples > llStartTimeInSamples)
  1667.         return FALSE ;
  1668. #endif    
  1669.     // now go through the rest of packets, and make sure they are contiguous until
  1670.     // the end of our mix time
  1671.     // If packets overlap, one packet will then take precedence over another -- not
  1672.     // much we can do about that.
  1673.     while( lp )
  1674.     {
  1675. pInfo    = (HXAudioInfo*) m_pDataList->GetNext(lp);
  1676.         // if we see a packet with a timestamp after the mix time ("future packet")
  1677.         // or one that does not abut with the previous one ("discontinuity"), stop.
  1678.         if (pInfo->llStartTimeInSamples >= llEndTimeInSamples)         // future packet
  1679. //            pInfo->llStartTimeInSamples != pInfoOld->llEndTimeInSamples) // discontinuity
  1680.         {
  1681.             break ;
  1682.         }
  1683.         pInfoOld = pInfo ;
  1684.     }
  1685.     // pInfoOld is the last packet to be mixed (or the last before a discontinuity).
  1686.     // Make sure it overlaps with the end of the mix buffer.
  1687.     if (pInfoOld->llEndTimeInSamples < llEndTimeInSamples)
  1688.     {
  1689.         llStartTimeInSamples = pInfoOld->llEndTimeInSamples ;
  1690.         nSamplesRequired = INT64_TO_UINT32(llEndTimeInSamples - llStartTimeInSamples) ;
  1691.         return FALSE ;
  1692.     }
  1693.     return TRUE ; // Data available!
  1694. }
  1695. HX_RESULT    
  1696. CHXAudioStream::StartCrossFade(CHXAudioStream*  pFromStream, 
  1697.        UINT32 ulCrossFadeStartTime,
  1698.        UINT32 ulCrossFadeDuration, 
  1699.        BOOL bToStream)
  1700. {
  1701. #if defined(HELIX_FEATURE_CROSSFADE)
  1702.     // XXX wschildbach need to account for rollover.
  1703.     INT64 llStartTimeInSamples = CAST_TO_INT64(ulCrossFadeStartTime) * m_DeviceFmt.ulSamplesPerSec / 1000 * m_DeviceFmt.uChannels ;
  1704.     INT64 llEndTimeInSamples = (CAST_TO_INT64(ulCrossFadeStartTime)+ulCrossFadeDuration) * m_DeviceFmt.ulSamplesPerSec / 1000 * m_DeviceFmt.uChannels ;
  1705.     m_pMixEngine->SetCrossFade(bToStream ? HXAudioSvcMixEngine::FADE_IN : HXAudioSvcMixEngine::FADE_OUT,
  1706.         llStartTimeInSamples, llEndTimeInSamples) ;
  1707. /*
  1708.     {
  1709.         FILE *f2 = fopen("c:\temp\mix.txt","a+");
  1710.         fprintf(f2,"** StartCrossFade(%I64d, %I64d, len=%ld, to=%sn",
  1711.             llStartTimeInSamples,
  1712.             llEndTimeInSamples,
  1713.             (INT32)(-llStartTimeInSamples+llEndTimeInSamples),
  1714.             bToStream?"yes":"no");
  1715.         fclose(f2);
  1716.     }
  1717. */
  1718.     return HXR_OK;
  1719. #else
  1720.     return HXR_NOTIMPL;
  1721. #endif /* HELIX_FEATURE_CROSSFADE */
  1722. }
  1723. /*
  1724.  *  IHXRealAudioSync methods
  1725.  */
  1726. /************************************************************************
  1727.  *  Method:
  1728.  *      IHXRealAudioSync::Register
  1729.  *  Purpose:
  1730.  */
  1731. STDMETHODIMP
  1732. CHXAudioStream::Register(void) 
  1733. {
  1734. #if defined _DEBUG && defined HELIX_FEATURE_AUDIO_MULTIPLAYER_PAUSE 
  1735.     if (HXDebugOptionEnabled("zDoNotUseFudge"))
  1736.     {
  1737.         return HXR_OK;
  1738.     }
  1739. #endif
  1740.     if (m_bRealAudioStream)
  1741.     {
  1742. return HXR_UNEXPECTED;
  1743.     }
  1744.     m_bRealAudioStream = TRUE;
  1745.     m_Owner->RegisterRealAudioStream(this);
  1746. #if defined(HELIX_FEATURE_AUDIO_INACCURATESAMPLING)
  1747.     if (!m_pRAByToTsInList)
  1748.     {
  1749. m_pRAByToTsInList = new CHXSimpleList;
  1750. m_pRAByToTsAdjustedList = new CHXSimpleList;
  1751.     }
  1752. #endif /* HELIX_FEATURE_AUDIO_INACCURATESAMPLING */
  1753.     return HXR_OK;
  1754. }
  1755. /************************************************************************
  1756.  *  Method:
  1757.  *      IHXRealAudioSync::UnRegister
  1758.  *  Purpose:
  1759.  */
  1760. STDMETHODIMP
  1761. CHXAudioStream::UnRegister(void)
  1762. {
  1763. #if defined _DEBUG && defined HELIX_FEATURE_AUDIO_MULTIPLAYER_PAUSE 
  1764.     if (HXDebugOptionEnabled("zDoNotUseFudge"))
  1765.     {
  1766.         return HXR_OK;
  1767.     }
  1768. #endif
  1769.     if (!m_bRealAudioStream)
  1770.     {
  1771. return HXR_UNEXPECTED;
  1772.     }
  1773.     m_bRealAudioStream = FALSE;
  1774.     m_Owner->UnRegisterRealAudioStream(this);
  1775.     CleanupRAByToTs();
  1776.     return HXR_OK;
  1777. }
  1778. /************************************************************************
  1779.  *  Method:
  1780.  *      IHXRealAudioSync::FudgeTimestamp
  1781.  *  Purpose:
  1782.  * Tell the audio stream about the relationship between the number 
  1783.  * of bytes written to the actual timestamp.
  1784.  *     
  1785.  */
  1786. STDMETHODIMP
  1787. CHXAudioStream::FudgeTimestamp(UINT32 /*IN*/ ulNumberofBytes,
  1788.        UINT32 /*IN*/ ulTimestamp)
  1789. {
  1790. #if defined(HELIX_FEATURE_AUDIO_INACCURATESAMPLING)
  1791. #if defined _DEBUG && defined HELIX_FEATURE_AUDIO_MULTIPLAYER_PAUSE 
  1792.     if (HXDebugOptionEnabled("zDoNotUseFudge"))
  1793.     {
  1794.         return HXR_OK;
  1795.     }
  1796. #endif
  1797.     RealAudioBytesToTimeStamp* pByToTs = 
  1798. new RealAudioBytesToTimeStamp;
  1799.     pByToTs->m_ulTimestamp = ulTimestamp;
  1800.     pByToTs->m_ulInTimestamp = m_ulLastInputStartTime;
  1801.     pByToTs->m_ulInEndTime = m_ulLastInputEndTime;
  1802.     if (m_bIsLive && m_ulBaseTime > 0)
  1803.     {
  1804. pByToTs->m_ulTimestamp += m_ulLiveDelay;
  1805. if (pByToTs->m_ulTimestamp > m_ulBaseTime)
  1806. {
  1807.     pByToTs->m_ulTimestamp -= m_ulBaseTime;
  1808. }
  1809. else
  1810. {
  1811.     pByToTs->m_ulTimestamp  = 0;
  1812. }
  1813.     }
  1814.     pByToTs->m_ulOrigTimestamp = pByToTs->m_ulTimestamp;
  1815.     m_pRAByToTsInList->AddTail((void*) pByToTs);
  1816. #endif /* HELIX_FEATURE_AUDIO_INACCURATESAMPLING */
  1817. //{FILE* f1 = ::fopen("d:\temp\audio.txt", "a+"); ::fprintf(f1, "Fudge:t%lut%lun", ulTimestamp, m_ulLastInputStartTime);::fclose(f1);}
  1818.     return HXR_OK;
  1819. }
  1820. void
  1821. CHXAudioStream::CleanupRAByToTs(void)
  1822. {
  1823. #if defined(HELIX_FEATURE_AUDIO_INACCURATESAMPLING)
  1824.     if (!m_pRAByToTsInList)
  1825.     {
  1826. return;
  1827.     }
  1828.     CHXSimpleList::Iterator ndx = m_pRAByToTsInList->Begin();
  1829.     for (; ndx != m_pRAByToTsInList->End(); ++ndx)
  1830.     {
  1831. RealAudioBytesToTimeStamp* pByToTs = 
  1832.     (RealAudioBytesToTimeStamp*) (*ndx);
  1833. delete pByToTs;
  1834.     }
  1835.     m_pRAByToTsInList->RemoveAll();
  1836.     ndx = m_pRAByToTsAdjustedList->Begin();
  1837.     for (; ndx != m_pRAByToTsAdjustedList->End(); ++ndx)
  1838.     {
  1839. RealAudioBytesToTimeStamp* pByToTs = 
  1840.     (RealAudioBytesToTimeStamp*) (*ndx);
  1841. delete pByToTs;
  1842.     }
  1843.     m_pRAByToTsAdjustedList->RemoveAll();
  1844. #endif /* HELIX_FEATURE_AUDIO_INACCURATESAMPLING */
  1845. HX_RESULT
  1846. CHXAudioStream::ConvertCurrentTime(double dBytesPlayed, 
  1847.    UINT32 ulCurrentTime, 
  1848.    UINT32& ulAdjustedTime)
  1849. {
  1850. #if defined(HELIX_FEATURE_AUDIO_INACCURATESAMPLING)
  1851.     HX_ASSERT(m_bRealAudioStream);
  1852.     ulAdjustedTime  = ulCurrentTime;
  1853.     LISTPOSITION posRABytes = m_pRAByToTsAdjustedList->GetHeadPosition();
  1854.     RealAudioBytesToTimeStamp* pByToTsLower = NULL;
  1855.     RealAudioBytesToTimeStamp* pByToTsHigher = NULL;
  1856.     INT64   llActualByToTsHigherTimestamp = 0;
  1857.     INT64   llActualByToTsLowerTimestamp =0;
  1858.     while(posRABytes)
  1859.     {
  1860. RealAudioBytesToTimeStamp* pByToTs = (RealAudioBytesToTimeStamp*) 
  1861.     m_pRAByToTsAdjustedList->GetAt(posRABytes);
  1862. if (dBytesPlayed >= pByToTs->m_ulOutNumBytes)
  1863. {
  1864.     pByToTsLower = pByToTs;
  1865. }
  1866. else
  1867. {
  1868.     if (pByToTsLower)
  1869.     {
  1870. pByToTsHigher = pByToTs; 
  1871.     }
  1872.     else
  1873.     {
  1874. /* It means that this stream was added mid-presentation and we have not yet 
  1875.  * played any bits from this stream. Maintain the current time and do not
  1876.  * fudge it.
  1877.  */ 
  1878. return HXR_OK;
  1879.     }
  1880. }
  1881. if (pByToTsLower && pByToTsHigher)
  1882. {
  1883.     break;
  1884. }
  1885. m_pRAByToTsAdjustedList->GetNext(posRABytes);
  1886.     }
  1887.     /* We got a range, interpolate */
  1888.     if (pByToTsLower && pByToTsHigher)
  1889.     {
  1890. //{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);}
  1891. /* Need to re-visit this ASSERT. A check will do for now */
  1892. #if 0
  1893. HX_ASSERT((pByToTsHigher->m_ulTimestamp >= 
  1894.    pByToTsLower->m_ulTimestamp) &&
  1895.   (pByToTsHigher->m_ulOutNumBytes >= 
  1896.    pByToTsLower->m_ulOutNumBytes));
  1897. #endif
  1898. llActualByToTsHigherTimestamp = CAST_TO_INT64 (pByToTsHigher->m_ulTimestamp) + CAST_TO_INT64 m_ulTSRollOver * CAST_TO_INT64 MAX_UINT32;
  1899. llActualByToTsLowerTimestamp = CAST_TO_INT64 (pByToTsLower->m_ulTimestamp) + CAST_TO_INT64 m_ulTSRollOver * CAST_TO_INT64 MAX_UINT32;
  1900. if ((llActualByToTsHigherTimestamp >= llActualByToTsLowerTimestamp) &&
  1901.     (pByToTsHigher->m_ulOutNumBytes >= pByToTsLower->m_ulOutNumBytes))
  1902. {
  1903.     ulAdjustedTime = pByToTsLower->m_ulTimestamp +
  1904.  (UINT32) (((dBytesPlayed - pByToTsLower->m_ulOutNumBytes)*1./
  1905.  (pByToTsHigher->m_ulOutNumBytes - 
  1906.   pByToTsLower->m_ulOutNumBytes)) *
  1907.   INT64_TO_UINT32(llActualByToTsHigherTimestamp - 
  1908.   llActualByToTsLowerTimestamp));
  1909. //{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);}
  1910. }
  1911. else 
  1912. {
  1913.     ulAdjustedTime = pByToTsLower->m_ulTimestamp;
  1914. //{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);}
  1915. }
  1916. //{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);}
  1917.     }
  1918.     /* The best we can do is return the time of the nearest map */
  1919.     else if (pByToTsLower)
  1920.     {
  1921. ulAdjustedTime = pByToTsLower->m_ulTimestamp;
  1922. double dBytesDiff = dBytesPlayed - pByToTsLower->m_ulOutNumBytes;
  1923. if (dBytesDiff > 0)
  1924. {
  1925.     double dNumBytes = m_Owner->ConvertMsToBytes(pByToTsLower->m_ulDuration);
  1926.     if (dBytesDiff >= dNumBytes)
  1927.     {
  1928. ulAdjustedTime += pByToTsLower->m_ulDuration;
  1929.     }
  1930.     else
  1931.     {
  1932. ulAdjustedTime += (UINT32) (pByToTsLower->m_ulDuration * dBytesDiff *1./dNumBytes);
  1933.     }
  1934. }
  1935. //{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);}
  1936.     }
  1937.     /* Remove all maps before pByToTsLower */
  1938.     posRABytes = m_pRAByToTsAdjustedList->GetHeadPosition();
  1939.     while(posRABytes)
  1940.     {
  1941. RealAudioBytesToTimeStamp* pByToTs = 
  1942.     (RealAudioBytesToTimeStamp*) m_pRAByToTsAdjustedList->GetAt(posRABytes);
  1943. if (pByToTs != pByToTsLower)
  1944. {
  1945. //{FILE* f1 = ::fopen("d:\temp\rasync.txt", "a+"); ::fprintf(f1, "Delete: OutBytes: %f OutTS: %lun", pByToTs->m_ulOutNumBytes, pByToTs->m_ulTimestamp);::fclose(f1);}
  1946.     delete pByToTs;
  1947.     posRABytes = m_pRAByToTsAdjustedList->RemoveAt(posRABytes);
  1948. }
  1949. else
  1950. {
  1951.     break;
  1952. }
  1953.     }
  1954. #else
  1955.     ulAdjustedTime = ulCurrentTime;
  1956. #endif /* HELIX_FEATURE_AUDIO_INACCURATESAMPLING */
  1957. //{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);}
  1958.     return HXR_OK;
  1959. }
  1960. void
  1961. CHXAudioStream::MapFudgedTimestamps(void)
  1962. {
  1963. #if defined(HELIX_FEATURE_AUDIO_INACCURATESAMPLING)
  1964.     LISTPOSITION    posRABytes = m_pRAByToTsInList->GetHeadPosition();
  1965.     INT64     llActualByToTsInEndTime = 0;
  1966.     INT64     llActualByToTsInStartTime = 0;
  1967.     
  1968.     while(posRABytes)
  1969.     {
  1970. RealAudioBytesToTimeStamp* pByToTs = 
  1971.     (RealAudioBytesToTimeStamp*) m_pRAByToTsInList->GetAt(posRABytes);
  1972. llActualByToTsInStartTime = CAST_TO_INT64 (pByToTs->m_ulInTimestamp) + CAST_TO_INT64 m_ulTSRollOver * CAST_TO_INT64 MAX_UINT32;
  1973. llActualByToTsInEndTime = CAST_TO_INT64 (pByToTs->m_ulInEndTime) + CAST_TO_INT64 m_ulTSRollOver * CAST_TO_INT64 MAX_UINT32;
  1974. /* Too late */
  1975. if (llActualByToTsInEndTime < m_llLastWriteTime)
  1976. {
  1977.     posRABytes = m_pRAByToTsInList->RemoveAt(posRABytes);
  1978.     delete pByToTs;
  1979. }
  1980. else if (llActualByToTsInStartTime <= m_llLastWriteTime
  1981. /*&& pByToTs->m_ulInEndTime >= m_ulLastStartTimePlayed*/)
  1982. {
  1983.     /* These two values will be used in determining what time it is */
  1984.     // Number of bytes that have been written to the audio device till now*/
  1985.     pByToTs->m_ulOutNumBytes = m_Owner->NumberOfBytesWritten();
  1986.  
  1987.     HX_ASSERT(m_llLastWriteTime - llActualByToTsInStartTime < MAX_TIMESTAMP_GAP);
  1988.     /* Interpolate */
  1989.     UINT32 ulTimeDiff = INT64_TO_UINT32(m_llLastWriteTime - llActualByToTsInStartTime);
  1990.     pByToTs->m_ulTimestamp += ulTimeDiff;
  1991.     
  1992.     pByToTs->m_ulDuration   = INT64_TO_UINT32((llActualByToTsInEndTime - 
  1993.        llActualByToTsInStartTime) - CAST_TO_INT64 ulTimeDiff);
  1994.       
  1995.     posRABytes = m_pRAByToTsInList->RemoveAt(posRABytes);
  1996.     m_pRAByToTsAdjustedList->AddTail(pByToTs);
  1997. //{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);}
  1998. }
  1999. else
  2000. {
  2001.     break;
  2002. }
  2003.     }
  2004. #endif /* HELIX_FEATURE_AUDIO_INACCURATESAMPLING */
  2005. }
  2006. // XXX wschildbach: How to implement this with the 64-bit timestamps?
  2007. void
  2008. CHXAudioStream::UpdateStreamLastWriteTime(BOOL bForceUpdate /*= FALSE*/)
  2009. {
  2010.     if (m_bLastWriteTimeUpdated)
  2011.     {
  2012. return;
  2013.     }
  2014.     m_bLastWriteTimeUpdated = TRUE;
  2015.     if (m_bIsLive)
  2016.     {
  2017. if (!m_pValues || m_pValues->GetPropertyULONG32("LiveSyncStartTime", m_ulBaseTime) != HXR_OK)
  2018. {
  2019.     if (bForceUpdate)
  2020.     {
  2021. m_bIsLive = FALSE;
  2022. m_ulBaseTime = 0;
  2023. m_llLastWriteTime = m_Owner->GetLastAudioWriteTime();
  2024.     }
  2025.     else
  2026.     {
  2027. /* 
  2028.  * do not set it yet.. we will wait till the first
  2029.  * AddData call 
  2030.  */
  2031. m_bLastWriteTimeUpdated = FALSE;
  2032.     }
  2033. }
  2034. else
  2035. {
  2036.     m_pValues->GetPropertyULONG32("Delay", m_ulLiveDelay);
  2037.     INT64 llLastPlayerWriteTime = m_Owner->GetLastAudioWriteTime();
  2038.     if (m_ulLiveDelay > 0 && 
  2039. CAST_TO_INT64  m_ulLiveDelay > llLastPlayerWriteTime &&
  2040. m_ulBaseTime > INT64_TO_UINT32(CAST_TO_INT64  m_ulLiveDelay-llLastPlayerWriteTime))
  2041.     {
  2042. m_llLastWriteTime   = CAST_TO_INT64 (m_ulBaseTime - 
  2043. INT64_TO_UINT32(CAST_TO_INT64  m_ulLiveDelay-llLastPlayerWriteTime));
  2044.     }
  2045.     else
  2046.     {
  2047. m_llLastWriteTime   = CAST_TO_INT64  m_ulBaseTime;
  2048.     }
  2049. }
  2050.     }
  2051.     else
  2052.     {
  2053. // XXX HP
  2054. //
  2055. // Prolbem: 
  2056. // when rewinding audio data upon resume, the audio-push-down worth of data
  2057. // would be missing when the stream's first resume is at the middle of playback,
  2058. // 
  2059. // Solution: 
  2060. // we need to adjust the m_llLastWriteTime to the last audio player write time
  2061. if (m_Owner->IsResumed() && !m_bIsResumed)
  2062. {
  2063.     m_llLastWriteTime = m_Owner->GetLastAudioWriteTime();
  2064. }
  2065.     }
  2066.     if (m_bLastWriteTimeUpdated && m_pMixEngine)
  2067.         m_pMixEngine->ResetTimeLineInMillis(m_llLastWriteTime) ;
  2068. }
  2069. void
  2070. CHXAudioStream::SaveLastNMilliSeconds(BOOL bSave, UINT32 ulNMilliSeconds)
  2071. {
  2072.     m_bLastNMilliSecsToBeSaved = bSave; // TRUE; //
  2073.     m_ulLastNMilliSeconds = ulNMilliSeconds; // 2000;//
  2074.     HX_ASSERT(!m_bLastNMilliSecsToBeSaved || m_ulLastNMilliSeconds > 0);
  2075.     // ensure we need to save for atleast 1 sec
  2076.     if (m_bLastNMilliSecsToBeSaved && m_ulLastNMilliSeconds < 1000)
  2077.     {
  2078. m_ulLastNMilliSeconds = 1000; 
  2079.     }
  2080.     if (!m_bLastNMilliSecsToBeSaved)
  2081.     {
  2082. while (m_pLastNMilliSecsList && m_pLastNMilliSecsList->GetCount() > 0)
  2083. {
  2084.     HXAudioInfo* pInfo = (HXAudioInfo*) m_pLastNMilliSecsList->RemoveHead();
  2085.     FreeInfo(pInfo);
  2086. }
  2087. HX_DELETE(m_pLastNMilliSecsList);
  2088.     }
  2089. }
  2090. void
  2091. CHXAudioStream::RewindStream(UINT32 ulTimeToRewind)
  2092. {
  2093.     HX_ASSERT(m_bLastNMilliSecsToBeSaved);
  2094.     if (!m_bCanBeRewound)
  2095.     {
  2096. return;
  2097.     }
  2098.     if (m_bLastNMilliSecsToBeSaved && m_pLastNMilliSecsList)
  2099.     {
  2100. HX_ASSERT(m_llLastWriteTime >= ulTimeToRewind);
  2101. if (m_llLastWriteTime >= ulTimeToRewind)
  2102. {
  2103.     m_llLastWriteTime -= ulTimeToRewind;
  2104. }
  2105. else
  2106. {
  2107.     m_llLastWriteTime = 0;
  2108. }
  2109. HXAudioInfo* pInfo = NULL;
  2110. // reset any pInfo's in data list that may have been partially used.
  2111. CHXSimpleList::Iterator ndx = m_pDataList->Begin();
  2112. for (; ndx != m_pDataList->End(); ++ndx)
  2113. {
  2114.     pInfo = (HXAudioInfo*) (*ndx);
  2115.     if (pInfo->ulBytesLeft  != pInfo->pBuffer->GetSize())
  2116.     {
  2117. pInfo->pOffset     = pInfo->pBuffer->GetBuffer();
  2118. pInfo->ulBytesLeft  = pInfo->pBuffer->GetSize();
  2119.     }
  2120.     else
  2121.     {
  2122. break;
  2123.     }
  2124. }
  2125. UINT32 ulLastWriteTime = INT64_TO_UINT32(m_llLastWriteTime - CAST_TO_INT64 m_ulTSRollOver * CAST_TO_INT64 MAX_UINT32);
  2126. BOOL bTimedToBeSet = (m_pLastNMilliSecsList->GetCount() > 0);
  2127. while (m_pLastNMilliSecsList->GetCount() > 0)
  2128. {
  2129.     pInfo = (HXAudioInfo*) m_pLastNMilliSecsList->RemoveTail();
  2130.     m_pDataList->AddHead(pInfo);
  2131.     if (pInfo->ulStartTime <= ulLastWriteTime)
  2132.     {
  2133. break;
  2134.     }
  2135. }
  2136. if (bTimedToBeSet)
  2137. {
  2138.     pInfo = (HXAudioInfo*) m_pDataList->GetHead();
  2139.     pInfo->uAudioStreamType = TIMED_AUDIO;
  2140. }
  2141. // remove remaining elements from the list
  2142. while (m_pLastNMilliSecsList->GetCount() > 0)
  2143. {
  2144.     pInfo = (HXAudioInfo*) m_pLastNMilliSecsList->RemoveHead();
  2145.     // delete the stored one
  2146.     HX_RELEASE(pInfo->pBuffer);
  2147.     HX_DELETE(pInfo);
  2148. }
  2149. //{FILE* f1 = ::fopen("d:\temp\multi.txt", "a+"); ::fprintf(f1, "%p %p RewindStream %lu %lun", this, m_Owner, ulFirstAudioTime, m_llLastWriteTime);::fclose(f1);}
  2150. #if defined(HELIX_FEATURE_AUDIO_INACCURATESAMPLING)
  2151. // put back stuff from adjusted list to in list
  2152. while (m_pRAByToTsAdjustedList && m_pRAByToTsAdjustedList->GetCount() > 0)
  2153. {
  2154.     RealAudioBytesToTimeStamp* pByToTs = 
  2155. (RealAudioBytesToTimeStamp*) m_pRAByToTsAdjustedList->RemoveTail();
  2156.     // restore original fudge timestamp
  2157.     pByToTs->m_ulTimestamp = pByToTs->m_ulOrigTimestamp;
  2158.     m_pRAByToTsInList->AddHead(pByToTs);
  2159. }
  2160. #endif /* HELIX_FEATURE_AUDIO_INACCURATESAMPLING */
  2161.     }
  2162. }
  2163. void
  2164. CHXAudioStream::Pause(BOOL bPlayerPause)
  2165. {
  2166.     if (m_eState == E_PAUSED)
  2167.     {
  2168. return;
  2169.     }
  2170.     m_eState = E_PAUSED;
  2171.     m_bCanBeRewound = FALSE;
  2172.     m_bPlayerPause = bPlayerPause;
  2173.     return;
  2174. }
  2175. void
  2176. CHXAudioStream::Resume(BOOL bPlayerResume)
  2177. {
  2178.     if (!m_bInited ||
  2179. m_eState == E_PLAYING)
  2180.     {
  2181. return;
  2182.     }
  2183.     UpdateStreamLastWriteTime();
  2184.     // resetting mixengine time line is done in UpdateStreamLastWriteTime()
  2185.     // add/resume audio stream on the fly
  2186.     if( m_Owner->IsResumed() )
  2187.     {
  2188.         
  2189.         if (m_eState != E_PAUSED &&
  2190.             !m_bIsResumed && 
  2191.             (!m_pDataList->IsEmpty() || !m_pInstantaneousList->IsEmpty()))
  2192.         {
  2193.             m_Owner->AudioStreamStateChanged(E_PLAYING);
  2194.             m_eState = E_PLAYING;
  2195.         }
  2196.         // whoever pause the stream is responsible for resuming the same
  2197.         // stream, the stream can either be paused specifically by the SMIL renderer
  2198.         // without pausing the playback or be paused by the AudioPlayer which
  2199.         // pauses the playback
  2200.         else if (!bPlayerResume || m_bPlayerPause)
  2201.         {
  2202.             m_eState = E_PLAYING;
  2203.         }
  2204.     }
  2205.     else
  2206.     {
  2207.         m_eState = E_PLAYING;
  2208.     }
  2209.     
  2210.     if (m_eState == E_PLAYING)
  2211.     {
  2212. m_bCanBeRewound = TRUE;       
  2213. m_bIsResumed = TRUE;
  2214.     }
  2215.  
  2216.     return;
  2217. }
  2218. void
  2219. CHXAudioStream::Seek(UINT32 ulSeekTime)
  2220. {
  2221.     m_llLastWriteTime = CAST_TO_INT64 (m_ulBaseTime + ulSeekTime);
  2222.     // XXX wschildbach: How to account for rollover?
  2223.     m_pMixEngine->ResetTimeLineInMillis(m_llLastWriteTime) ;
  2224.     m_bFirstWrite = TRUE;
  2225.     m_bTobeTimed = TRUE;
  2226.     m_ulTSRollOver = 0;
  2227.     m_ulLastInputStartTime  = 0;
  2228.     m_ulLastInputEndTime    = 0;
  2229.     // Remove all buffers from auxlliary list. This means that a 
  2230.     // renderer must send all buffers again including those buffers
  2231.     // that start at time t way out there in the future.
  2232.     FlushBuffers();
  2233.     CleanupRAByToTs();
  2234.     
  2235.     /* Remove any cross-fading */
  2236.     m_bCrossFadingToBeDone  = FALSE;
  2237.     HX_RELEASE(m_pCrossFadeStream);
  2238.     /* clear the pending bytes buffer */
  2239.     m_ulPendingAudioBytes = 0 ;
  2240.     HX_RELEASE(m_piPendingAudioData) ;
  2241.     // XXX HP what happen if this is called from the client core on
  2242.     //       IHXTrack::Seek()
  2243.     return;
  2244. }
  2245. void
  2246. CHXAudioStream::Stop(void)
  2247. {
  2248.     if (m_eState == E_STOPPED)
  2249.     {
  2250. return;
  2251.     }
  2252.     m_eState = E_STOPPED;
  2253.     ResetStream();
  2254.     return;
  2255. }
  2256. void
  2257. CHXAudioStream::SetAudioDeviceReflushHint(BOOL bSupported)
  2258. {
  2259.     m_bAudioDeviceReflushHint = bSupported;
  2260.     return;
  2261. }
  2262. void
  2263. CHXAudioStream::FreeInfo(HXAudioInfo* pInfo, BOOL bInstantaneous /* = FALSE */)
  2264. {
  2265.     if (m_bLastNMilliSecsToBeSaved && !bInstantaneous)
  2266.     {
  2267. if (!m_pLastNMilliSecsList)
  2268. {
  2269.     m_pLastNMilliSecsList = new CHXSimpleList;
  2270.     m_ulLastNHeadTime = pInfo->ulStartTime;
  2271.     m_ulLastNTailTime = pInfo->ulStartTime;
  2272. }
  2273. // reset members
  2274. pInfo->pOffset     = pInfo->pBuffer->GetBuffer();
  2275. pInfo->ulBytesLeft  = pInfo->pBuffer->GetSize();
  2276. // add it to the tail
  2277. m_pLastNMilliSecsList->AddTail(pInfo);
  2278.         // Last m_ulLastNTailTime could have been invalidated by a rewind:
  2279.         // check it again here:
  2280.         m_ulLastNHeadTime = ((HXAudioInfo*) m_pLastNMilliSecsList->GetHead())->ulStartTime;
  2281. m_ulLastNTailTime = pInfo->ulStartTime;
  2282. // time to expire certain blocks?
  2283. if (CALCULATE_ELAPSED_TICKS(m_ulLastNHeadTime, m_ulLastNTailTime) > m_ulLastNMilliSeconds)
  2284. {
  2285.     // override pInfo. we will delete this block at the bottom
  2286.     pInfo = (HXAudioInfo*) m_pLastNMilliSecsList->RemoveHead();
  2287.     // update head time
  2288.     HXAudioInfo* pHeadInfo = (HXAudioInfo*) m_pLastNMilliSecsList->GetHead();
  2289.     // we should always have ATLEAST one nore in the list
  2290.     HX_ASSERT(pHeadInfo);
  2291.     m_ulLastNHeadTime = pHeadInfo->ulStartTime;
  2292. }
  2293. else
  2294. {
  2295.     // early exit to save this block
  2296.     return;
  2297. }
  2298.     }
  2299.     FreeBuffer(pInfo->pBuffer);
  2300.     delete pInfo;
  2301. }
  2302. void
  2303. CHXAudioStream::FreeBuffer(IHXBuffer* pBuffer)
  2304. {
  2305.     /* do we need to keep it around for reuse? */
  2306.     if (!m_pAvailableBuffers || m_pAvailableBuffers->GetCount() >= m_uCacheSize)
  2307.     {
  2308. //{FILE* f1 = ::fopen("d:\temp\cache.txt", "a+"); ::fprintf(f1, "Discard n");::fclose(f1);}
  2309. /* 
  2310.  * now that we are full, we should grow the cache size, if we ever have
  2311.  * a cache miss
  2312.  */
  2313. m_bCacheMayBeGrown = TRUE;
  2314. pBuffer->Release();
  2315. return;
  2316.     }
  2317.     /* 
  2318.      * check if we have the only reference, if so reuse it
  2319.      * else release our reference
  2320.      */
  2321.     pBuffer->AddRef();
  2322.     if (pBuffer->Release() > 1)
  2323.     {
  2324. pBuffer->Release();
  2325. return;
  2326.     }
  2327. #ifdef _MACINTOSH
  2328.     m_pAvailableBuffers->AddTail((void*) pBuffer);
  2329. #else
  2330.     BOOL bAddToTail = (HX_GET_BETTERTICKCOUNT() & 0x01) ? TRUE : FALSE;
  2331.     LISTPOSITION listRet = NULL;
  2332.     if (bAddToTail)
  2333.     {
  2334. listRet = m_pAvailableBuffers->AddTail((void*) pBuffer);
  2335.     }
  2336.     else
  2337.     {
  2338. listRet = m_pAvailableBuffers->AddHead((void*) pBuffer);
  2339.     }
  2340.     if( listRet == NULL )
  2341.     {
  2342.         m_wLastError = HXR_OUTOFMEMORY;
  2343.     }
  2344. #endif
  2345. //{FILE* f1 = ::fopen("d:\temp\cache.txt", "a+"); ::fprintf(f1, "Added %dn", m_pAvailableBuffers->GetCount());::fclose(f1);}
  2346.     return;
  2347. }
  2348. /************************************************************************
  2349.  *  Method:
  2350.  * IHXCommonClassFactory::CreateInstance
  2351.  */
  2352. STDMETHODIMP 
  2353. CHXAudioStream::CreateInstance
  2354. (
  2355.     REFCLSID /*IN*/ rclsid,
  2356.     void** /*OUT*/ ppUnknown
  2357. )
  2358. {
  2359.     HX_RESULT theErr = HXR_OK;
  2360.     if (IsEqualCLSID(rclsid, CLSID_IHXBuffer))
  2361.     {
  2362. if (!m_pAvailableBuffers)
  2363. {
  2364. #ifdef _MACINTOSH
  2365.     m_pAvailableBuffers = new HXAudioMacQueue;
  2366. #else
  2367.     m_pAvailableBuffers = new CHXSimpleList;
  2368. #endif
  2369. }
  2370. if (m_pAvailableBuffers->GetCount() > 0)
  2371. {
  2372. #ifdef _MACINTOSH
  2373.     *ppUnknown = (IUnknown*) (IHXBuffer*) m_pAvailableBuffers->RemoveHead();
  2374.     if (!*ppUnknown) goto justincase;
  2375. #else
  2376.     BOOL bRemoveFromHead = (HX_GET_BETTERTICKCOUNT() & 0x01) ? TRUE : FALSE;
  2377.     if (bRemoveFromHead)
  2378.     {
  2379. *ppUnknown = (IUnknown*) (IHXBuffer*) m_pAvailableBuffers->RemoveHead();
  2380.     }
  2381.     else
  2382.     {
  2383. *ppUnknown = (IUnknown*) (IHXBuffer*) m_pAvailableBuffers->RemoveTail();
  2384.     }
  2385. #endif
  2386. //{FILE* f1 = ::fopen("d:\temp\cache.txt", "a+"); ::fprintf(f1, "Cache Hit %dn", m_pAvailableBuffers->GetCount());::fclose(f1);}
  2387.     goto exit;
  2388. }
  2389. else
  2390. {
  2391. #ifdef _MACINTOSH
  2392. justincase:
  2393. #endif
  2394.     if (m_bCacheMayBeGrown)
  2395.     {
  2396. m_bCacheMayBeGrown  = FALSE;
  2397. m_uCacheSize     += CACHE_INCREMENT_SIZE;
  2398. //{FILE* f1 = ::fopen("d:\temp\cache.txt", "a+"); ::fprintf(f1, "Cache increased to: %u n", m_uCacheSize);::fclose(f1);}
  2399.     }
  2400. }
  2401. /* 
  2402.  * fall down to using the comonclass factory to allocate this buiffer since 
  2403.  * we do not have it in the cache
  2404.  */
  2405.     }
  2406. //{FILE* f1 = ::fopen("d:\temp\cache.txt", "a+"); ::fprintf(f1, "Cache Miss buffered blocks: %dn", m_pDataList->GetCount());::fclose(f1);}
  2407.     theErr = m_pCommonClassFactory->CreateInstance(rclsid, ppUnknown);
  2408. exit:
  2409.     return theErr;
  2410. }
  2411. /************************************************************************
  2412.  *  Method:
  2413.  * IHXCommonClassFactory::CreateInstanceAggregatable
  2414.  */
  2415. STDMETHODIMP 
  2416. CHXAudioStream::CreateInstanceAggregatable
  2417. (
  2418.     REFCLSID     /*IN*/ rclsid,
  2419.     REF(IUnknown*)  /*OUT*/ pUnknown,
  2420.     IUnknown*     /*IN*/ pUnkOuter
  2421. )
  2422. {
  2423.     return m_pCommonClassFactory->CreateInstanceAggregatable(rclsid, pUnknown, pUnkOuter);
  2424. }
  2425. // XXX wschildbach: What does this method do? How do we implement this with
  2426. // the 64-bit timestamps?
  2427. /************************************************************************
  2428.  * Method:
  2429.  *     IHXUpdateProperties::UpdatePacketTimeOffset
  2430.  * Purpose:
  2431.  *     Call this method to update the timestamp offset of cached packets
  2432.  */
  2433. STDMETHODIMP
  2434. CHXAudioStream::UpdatePacketTimeOffset(INT32 lTimeOffset)
  2435. {
  2436.     HX_RESULT     rc = HXR_OK;
  2437.     HXAudioInfo*    pInfo = NULL;
  2438.     // adjust the start time 
  2439.     CHXSimpleList::Iterator ndx = m_pDataList->Begin();
  2440.     for (; ndx != m_pDataList->End(); ++ndx)
  2441.     {
  2442. pInfo = (HXAudioInfo*) (*ndx);
  2443. pInfo->ulStartTime += lTimeOffset;
  2444.     }
  2445.   
  2446.     if (m_pLastNMilliSecsList)
  2447.     {
  2448. ndx = m_pLastNMilliSecsList->Begin();
  2449. for (; ndx != m_pLastNMilliSecsList->End(); ++ndx)
  2450. {
  2451.     pInfo = (HXAudioInfo*) (*ndx);
  2452.     pInfo->ulStartTime += lTimeOffset;
  2453. }
  2454.     }
  2455.     // Adjust more state:
  2456.     m_ulLastInputStartTime += lTimeOffset;
  2457.     m_ulLastInputEndTime += lTimeOffset;  
  2458.     m_llLastWriteTime += lTimeOffset;
  2459.     m_ulLastNHeadTime += lTimeOffset;  
  2460.     m_ulLastNTailTime += lTimeOffset;  
  2461.     m_llLastStartTimePlayed += lTimeOffset;
  2462.     return rc;
  2463. }
  2464. /************************************************************************
  2465.  * Method:
  2466.  *     IHXUpdateProperties::UpdatePlayTimes
  2467.  * Purpose:
  2468.  *     Call this method to update the playtime attributes
  2469.  */
  2470. STDMETHODIMP
  2471. CHXAudioStream::UpdatePlayTimes(IHXValues* pProps)
  2472. {
  2473.     return HXR_OK;
  2474. }
  2475. // XXX wschildbach: How do we implement this method with 64-bit timestamps?
  2476. void
  2477. CHXAudioStream::RollBackTimestamp()
  2478. {
  2479.     if (m_llLastWriteTime > CAST_TO_INT64 m_ulGranularity)
  2480.     {
  2481. m_llLastWriteTime -= CAST_TO_INT64 m_ulGranularity;
  2482.     }
  2483. }
  2484. #ifdef _MACINTOSH
  2485. /////////////////////////////////////////////////////////////////////////
  2486. //
  2487. // HXAudioMacQueue
  2488. //
  2489. // For passing data between an interrupt and anything else (mac only).
  2490. //
  2491. HXAudioMacQueue::HXAudioMacQueue()
  2492. {
  2493.   mQueueHeader.qFlags=0;
  2494. mQueueHeader.qHead=0;
  2495. mQueueHeader.qTail=0;
  2496. mDestructing = FALSE; // just a safety check
  2497. m_nCount = 0;
  2498. }
  2499. /////////////////////////////////////////////////////////////////////////
  2500. //
  2501. HX_RESULT HXAudioMacQueue::AddTail(void* pObject)
  2502. {
  2503. if (pObject && !mDestructing)
  2504. {
  2505.     HXAudioMacQueueElement * theElement = new HXAudioMacQueueElement();  
  2506.     
  2507.     if (theElement)
  2508.     {  
  2509.     theElement->mNextElementInQueue = NULL;
  2510.     theElement->mObject = pObject;    
  2511.     ::Enqueue((QElem *)theElement, &mQueueHeader);
  2512.     
  2513.     m_nCount++;
  2514.     
  2515.     //
  2516.     // If someone interrupts and enters the destructor while we're in here,
  2517.     // then the pObject and the new node will be leaked.  This shouldn't 
  2518.     // happen since we should have shut down all interrupts that would
  2519.     // be adding items to the queue long before we start destructing it.
  2520.     //
  2521.     
  2522.     HX_ASSERT(!mDestructing); // if we DID enter the destructor, let the programmer know...
  2523.     }
  2524.          
  2525.     return HXR_OK;
  2526.     }
  2527.         
  2528.     return HXR_FAILED;
  2529. }
  2530. /////////////////////////////////////////////////////////////////////////
  2531. //
  2532. void * HXAudioMacQueue::RemoveHead()
  2533. {
  2534. //
  2535.     // POINT A
  2536.     // 
  2537.     // You can look at the qHead anytime you want, but you can't USE a
  2538.     // pointer unless it's OFF of the queue.  Basically you do a
  2539.     // Dequeue, and if it succeeds then you know nobody else has it.
  2540.     // If it fails, an error is returned and you don't mess with it.
  2541.     //
  2542.     
  2543. if (mQueueHeader.qHead)
  2544.     {
  2545.   HXAudioMacQueueElement * theElement = (HXAudioMacQueueElement *) mQueueHeader.qHead;
  2546. if (theElement)
  2547. {
  2548. OSErr e = ::Dequeue( (QElemPtr) theElement, &mQueueHeader );
  2549. //
  2550. // Between points A and D, we can't be
  2551. // guaranteed that the queue header and
  2552. // theElement are valid.  But Dequeue will
  2553. // TELL us if that pointer is still valid by
  2554. // its return code.  If it can't remove the
  2555. // item from the queue, then somebody else did
  2556. // and the pointer is no longer ours.  If no
  2557. // error was returned from dequeue, then it's
  2558. // ours to mess with.
  2559. //
  2560. if (e == noErr)
  2561. {
  2562. // at this point we know that we can
  2563. // do whatever we need to with the
  2564. // object.
  2565. void* theObj = theElement->mObject;
  2566. delete theElement; // delete the node
  2567. m_nCount--;
  2568. HX_ASSERT(m_nCount >= 0);
  2569. return theObj;
  2570. }
  2571. }
  2572.    }
  2573.    
  2574.    return NULL;
  2575. }
  2576. /////////////////////////////////////////////////////////////////////////
  2577. //
  2578. UINT32 HXAudioMacQueue::GetCount()
  2579. {
  2580.     return m_nCount;
  2581. }
  2582. /////////////////////////////////////////////////////////////////////////
  2583. //
  2584. HXAudioMacQueue::~HXAudioMacQueue()
  2585. {
  2586. mDestructing = TRUE; // don't add anything else to the queue
  2587. void * theObject;
  2588. while ((theObject = RemoveHead()) != 0)
  2589.      {
  2590.      }
  2591.     
  2592.     // and just to be safe...
  2593. mQueueHeader.qHead=0;
  2594. mQueueHeader.qTail=0;
  2595. }
  2596. #endif