hxaudstr_new.cpp
上传用户:dangjiwu
上传日期:2013-07-19
资源大小:42019k
文件大小:89k
源码类别:

Symbian

开发平台:

Visual C++

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