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

Symbian

开发平台:

Visual C++

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Source last modified: $Id: hxaudply.cpp,v 1.25.2.3 2004/07/09 02:08:08 hubbe 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/stdlib.h"
  51. #include "hlxclib/string.h"
  52. #ifndef WIN32_PLATFORM_PSPC
  53. #include "hlxclib/signal.h"
  54. #endif
  55. #include "hxresult.h"
  56. #include "hxtypes.h"
  57. #include "hxslist.h"
  58. #include "hxcom.h"
  59. #include "ihxpckts.h"
  60. #include "hxengin.h"
  61. #include "hxprefs.h"
  62. #include "hxausvc.h"
  63. #include "hxrasyn.h"
  64. #include "hxerror.h"
  65. #include "hxcore.h"
  66. #include "hxmap.h"
  67. #include "hxaudply.h"
  68. #include "hxaudstr.h"
  69. #include "hxaudses.h"
  70. #include "hxaudvol.h"
  71. #include "hxaudtyp.h"
  72. #include "timeval.h"
  73. #include "hxtick.h"
  74. #ifdef _MACINTOSH
  75. #include "hxmm.h"
  76. extern  ULONG32 gTIMELINE_MUTEX;
  77. #endif
  78. #include "hxheap.h"
  79. #ifdef _DEBUG
  80. #undef HX_THIS_FILE
  81. static const char HX_THIS_FILE[] = __FILE__;
  82. #endif
  83. #if defined(HELIX_FEATURE_PREFERENCES)
  84. #include "hxprefs.h"
  85. #include "hxprefutil.h"
  86. #endif /* HELIX_FEATURE_PREFERENCES */
  87. #ifdef HELIX_CONFIG_MIN_PCM_PUSHDOWN_BYTES
  88. // These values were arrived at via trial and error on a linux, xi586 (pentium)
  89. #define MINIMUM_AUDIO_GRANULARITY 50
  90. #define MAXIMUM_AUDIO_GRANULARITY 100
  91. #else
  92. #define MINIMUM_AUDIO_GRANULARITY 50
  93. #define MAXIMUM_AUDIO_GRANULARITY 100
  94. #endif
  95. #define MAX_WAIT_AT_SAME_TIME (MAXIMUM_AUDIO_GRANULARITY+50)
  96. CHXAudioPlayer::CHXAudioPlayer( CHXAudioSession* owner )
  97. : m_lRefCount(0)
  98. ,       m_ulCallbackID(0)
  99. , m_pContext(0)
  100. , m_bInited(FALSE)
  101. , m_ulGranularity(0)
  102. , m_Owner(owner)
  103. , m_pStreamList(0)
  104. , m_pRealAudioStreamList(0)
  105. , m_bAdjustForRealAudio(FALSE)
  106. , m_pPlayerResponse(0)
  107. , m_pScheduler (0)
  108. , m_pPreferences(0)
  109. , m_bHasStreams(FALSE)
  110. , m_bIsLive(FALSE)
  111. , m_ulCurrentTime(0)
  112. , m_ulLastCurrentTimeReturned(0)
  113. , m_ulLastDeviceTimeAdjusted(0)
  114. , m_bTimeReturned(FALSE)
  115. , m_pPMixHookList(0)
  116. , m_ulBytesPerGran(0)
  117. , m_pStreamRespList(0)
  118. , m_ulASstartTime(0)
  119. , m_ulAPplaybackTime(0)
  120. , m_ulAPstartTime(0)
  121. , m_ulADresumeTime(0)
  122. ,  m_eState(E_STOPPED)
  123. , m_bPrefUse11khz(FALSE)
  124. , m_uPrefBitsPerSample(16)
  125. , m_uPrefAudioQuality(4)
  126. , m_pFakeAudioCBTime(0)
  127. , m_ulLastFakeCallbackTime(0)
  128. , m_ulIncreasingTimer(0)
  129. , m_bDisableWrite(FALSE)
  130. , m_bIsResumed(FALSE)
  131. , m_ulTimeAdjustDoneAt(0)
  132. , m_bIsDonePlayback(TRUE)
  133. , m_bIsFirstResume(TRUE)
  134. , m_bCanBeRewound(FALSE)
  135. , m_uVolume(0)
  136. , m_bMute(FALSE)
  137. , m_bHasDataInAudioDevice(FALSE)
  138. , m_llLastWriteTime(0)
  139. {
  140. #ifdef HELIX_FEATURE_VOLUME
  141.     m_pPlayerVolume = NULL;
  142. #endif
  143.     m_Owner->AddRef();
  144.     m_pFakeAudioCBTime = new Timeval;
  145.     // NOTE: we should add some check on the success of this allocation.
  146.     /* Default value of Player format */
  147.     m_PlayerFmt.uChannels = 2;
  148.     m_PlayerFmt.uBitsPerSample = 16;
  149.     m_PlayerFmt.ulSamplesPerSec = 16000;
  150.     m_PlayerFmt.uMaxBlockSize = 64000;
  151. }
  152. /************************************************************************
  153.  *  Method:
  154.  * IHXAudioPlayer::~CHXAudioPlayer()
  155.  * Purpose:
  156.  * Destructor. Clean up and set free.
  157.  */
  158. CHXAudioPlayer::~CHXAudioPlayer()
  159. {
  160.     Close();
  161. }
  162. void CHXAudioPlayer::Close(void)
  163. {
  164.     ResetPlayer();
  165.     HX_DELETE(m_pStreamList);
  166.     HX_DELETE(m_pRealAudioStreamList);
  167.     HX_RELEASE(m_pContext);
  168. #if defined(HELIX_FEATURE_PREFERENCES)
  169.     HX_RELEASE(m_pPreferences);
  170. #endif /* HELIX_FEATURE_PREFERENCES */
  171.     HX_RELEASE(m_pPlayerResponse);
  172.     if ( m_pPMixHookList )
  173.     {
  174. HXAudioHookInfo* pMixHookInfo  = 0;
  175. while(!m_pPMixHookList->IsEmpty())
  176. {
  177.     pMixHookInfo = (HXAudioHookInfo*) m_pPMixHookList->RemoveHead();
  178.     pMixHookInfo->pHook->Release();
  179.     delete pMixHookInfo;
  180. }
  181. delete m_pPMixHookList;
  182. m_pPMixHookList = 0;
  183.     }
  184.     // Delete all stream response items.
  185.     if ( m_pStreamRespList )
  186.     {
  187. IHXAudioStreamInfoResponse*   pAudioStreamInfoResponse = 0;
  188. while(!m_pStreamRespList->IsEmpty())
  189. {
  190.     pAudioStreamInfoResponse =
  191.      (IHXAudioStreamInfoResponse*) m_pStreamRespList->RemoveHead();
  192.     pAudioStreamInfoResponse->Release();
  193. }
  194. delete m_pStreamRespList;
  195. m_pStreamRespList = 0;
  196.     }
  197. #ifdef HELIX_FEATURE_VOLUME
  198.     if( m_pPlayerVolume )
  199.     {
  200.         m_pPlayerVolume->RemoveAdviseSink((IHXVolumeAdviseSink*)this);
  201.         m_pPlayerVolume->Release();
  202.         m_pPlayerVolume = NULL;
  203.     }
  204. #endif
  205.     // Delete IRMA volume object.
  206.     HX_DELETE(m_pFakeAudioCBTime);
  207.     HX_RELEASE(m_Owner);
  208.     HX_RELEASE(m_pScheduler);
  209. }
  210. /////////////////////////////////////////////////////////////////////////
  211. //  Method:
  212. //      IUnknown::QueryInterface
  213. //  Purpose:
  214. //      Implement this to export the interfaces supported by your
  215. //      object.
  216. //
  217. STDMETHODIMP CHXAudioPlayer::QueryInterface(REFIID riid, void** ppvObj)
  218. {
  219.     QInterfaceList qiList[] =
  220.         {
  221.             { GET_IIDHANDLE(IID_IHXAudioPlayer), (IHXAudioPlayer*)this },
  222. #if defined(HELIX_FEATURE_CROSSFADE)
  223.             { GET_IIDHANDLE(IID_IHXAudioCrossFade), (IHXAudioCrossFade*)this },
  224. #endif /* HELIX_FEATURE_CROSSFADE */
  225. #if defined(HELIX_FEATURE_VOLUME)
  226.             { GET_IIDHANDLE(IID_IHXVolumeAdviseSink), (IHXVolumeAdviseSink*)this },
  227. #endif /* HELIX_FEATURE_VOLUME */
  228.             { GET_IIDHANDLE(IID_IUnknown), (IUnknown*)(IHXAudioPlayer*)this },
  229.         };
  230.     HX_RESULT res = ::QIFind(qiList, QILISTSIZE(qiList), riid, ppvObj);
  231.     // if succeeded, return immediately...
  232.     if (SUCCEEDED(res))
  233.     {
  234.         return res;
  235.     }
  236.     // ...otherwise, proceed.
  237.     if (m_Owner &&
  238.      m_Owner->QueryInterface(riid, ppvObj) == HXR_OK)
  239.     {
  240. return HXR_OK;
  241.     }
  242.     *ppvObj = NULL;
  243.     return HXR_NOINTERFACE;
  244. }
  245. /////////////////////////////////////////////////////////////////////////
  246. //  Method:
  247. //      IUnknown::AddRef
  248. //  Purpose:
  249. //      Everyone usually implements this the same... feel free to use
  250. //      this implementation.
  251. //
  252. STDMETHODIMP_(ULONG32) CHXAudioPlayer::AddRef()
  253. {
  254.     return InterlockedIncrement(&m_lRefCount);
  255. }
  256. /////////////////////////////////////////////////////////////////////////
  257. //  Method:
  258. //      IUnknown::Release
  259. //  Purpose:
  260. //      Everyone usually implements this the same... feel free to use
  261. //      this implementation.
  262. //
  263. STDMETHODIMP_(ULONG32) CHXAudioPlayer::Release()
  264. {
  265.     if (InterlockedDecrement(&m_lRefCount) > 0)
  266.     {
  267.         return m_lRefCount;
  268.     }
  269.     delete this;
  270.     return 0;
  271. }
  272. /*
  273.  *  IHXAudioPlayer methods
  274.  */
  275. /************************************************************************
  276.  *  Method:
  277.  *              IHXAudioPlay::AddPostMixHook
  278.  *      Purpose:
  279.  *       Call this to add a post mix hook of the audio data.
  280.  */
  281. STDMETHODIMP CHXAudioPlayer::AddPostMixHook
  282. (
  283.     IHXAudioHook* pHook,
  284.     const BOOL bDisableWrite,
  285.     const BOOL          bFinal
  286. )
  287. {
  288.     /* We only allow adding Hooks before the playback has started */
  289.     if (m_bInited)
  290.     {
  291. return HXR_FAILED;
  292.     }
  293.     return ActualAddPostMixHook(pHook, bDisableWrite, bFinal);
  294. }
  295. /************************************************************************
  296.  *  Method:
  297.  *              IHXAudioPlay::RemovePostMixHook
  298.  *      Purpose:
  299.  *       Call this to remove a post mix hook.
  300.  */
  301. STDMETHODIMP CHXAudioPlayer::RemovePostMixHook
  302. (
  303.     IHXAudioHook* pHook
  304. )
  305. {
  306.     /* We only allow removing Hooks after the playback has stopped */
  307.     if (m_bInited)
  308.     {
  309. return HXR_FAILED;
  310.     }
  311.     return ActualRemovePostMixHook(pHook);
  312. }
  313. /************************************************************************
  314. *  Method:
  315. *      IHXAudioPlayer::GetAudioStreamCount
  316. *  Purpose:
  317. * Get the number of audio streams currently active in the
  318. * audio player. Since streams can be added mid-presentation
  319. * this function may return different values on different calls.
  320. * If the user needs to know about all the streams as they get
  321. * get added to the player, IHXAudioStreamInfoResponse should
  322. * be implemented and passed in SetStreamInfoResponse.
  323. */
  324. STDMETHODIMP_(UINT16) CHXAudioPlayer::GetAudioStreamCount()
  325. {
  326.     HX_ASSERT(m_pStreamList);
  327.     if (m_pStreamList)
  328.     {
  329. return (UINT16) m_pStreamList->GetCount();
  330.     }
  331.     else
  332.     {
  333. return 0;
  334.     }
  335. }
  336. /************************************************************************
  337.  *  Method:
  338.  *              IHXAudioPlayer::GetAudioStream
  339.  *      Purpose:
  340.  */
  341. STDMETHODIMP_(IHXAudioStream*) CHXAudioPlayer::GetAudioStream
  342. (
  343.     UINT16  uIndex
  344. )
  345. {
  346.     LISTPOSITION lp = 0;
  347.     lp = m_pStreamList->FindIndex( (int) uIndex );
  348.     if ( lp )
  349.     {
  350. CHXAudioStream* s = 0;
  351. s = (CHXAudioStream*) m_pStreamList->GetAt(lp);
  352. s->AddRef();
  353. return s;
  354.     }
  355.     else
  356.         return 0;
  357. }
  358. /************************************************************************
  359.  *  Method:
  360.  *              IHXAudioPlayer::GetAudioVolume
  361.  *      Purpose:
  362.  * Return this player's IRMA volume interface.
  363.  */
  364. STDMETHODIMP_(IHXVolume*) CHXAudioPlayer::GetAudioVolume()
  365. {
  366.     IHXVolume* pRet = NULL;
  367. #ifdef HELIX_FEATURE_VOLUME
  368.     if( m_pPlayerVolume )
  369.     {
  370.         m_pPlayerVolume->AddRef();
  371.         pRet = m_pPlayerVolume;
  372.     }
  373. #endif
  374.     return pRet;
  375. }
  376. /************************************************************************
  377.  *  Method:
  378.  *              IHXAudioPlayer::GetDeviceVolume
  379.  *      Purpose:
  380.  * Return this audio device volume interface.
  381.  */
  382. STDMETHODIMP_(IHXVolume*) CHXAudioPlayer::GetDeviceVolume()
  383. {
  384.     return ( m_Owner->GetDeviceVolume() );
  385. }
  386. /*
  387.  *  IHXAudioCrossFade methods
  388.  */
  389. /************************************************************************
  390.  *  Method:
  391.  *      IHXAudioCrossFade::CrossFade
  392.  *  Purpose:
  393.  * Cross-fade two audio streams.
  394.  * pStreamFrom - Stream to be cross faded from
  395.  * pStreamTo - Stream to be cross faded to
  396.  * ulFromCrossFadeStartTime- "From" Stream time when cross fade is
  397.  *   to be started
  398.  * ulToCrossFadeStartTime - "To" Stream time when cross fade is to
  399.  *   be started
  400.  * ulCrossFadeDuration - Duration over which cross-fade needs
  401.  *       to be done
  402.  *
  403.  */
  404. STDMETHODIMP
  405. CHXAudioPlayer::CrossFade(IHXAudioStream*  pStreamFrom,
  406.   IHXAudioStream*  pStreamTo,
  407.   UINT32     ulFromCrossFadeStartTime,
  408.   UINT32     ulToCrossFadeStartTime,
  409.   UINT32     ulCrossFadeDuration)
  410. {
  411. #if defined(HELIX_FEATURE_CROSSFADE)
  412.     HX_RESULT theErr = HXR_OK;
  413.     /* Make sure these two streams are available */
  414.     LISTPOSITION streamPos1 = m_pStreamList->Find(pStreamFrom);
  415.     LISTPOSITION streamPos2 = m_pStreamList->Find(pStreamTo);
  416.     if (!streamPos2 || !streamPos1)
  417.     {
  418. return HXR_INVALID_PARAMETER;
  419.     }
  420.     CHXAudioStream* pFromStream =
  421. (CHXAudioStream*) m_pStreamList->GetAt(streamPos1);
  422.     CHXAudioStream* pToStream =
  423. (CHXAudioStream*) m_pStreamList->GetAt(streamPos2);
  424.     theErr = pFromStream->StartCrossFade(pToStream, ulFromCrossFadeStartTime,
  425. ulCrossFadeDuration, FALSE);
  426.     if (!theErr)
  427.     {
  428. theErr = pToStream->StartCrossFade(pFromStream, ulToCrossFadeStartTime,
  429. ulCrossFadeDuration, TRUE);
  430.     }
  431.     /* Adjust the streams in list so that the "ToStream" List appears before
  432.      * the "FromStream"
  433.      * A lame but quick-and-dirty way to do it: Remove these two stream from
  434.      * the listand them in order at the tail!
  435.      */
  436.     if (!theErr)
  437.     {
  438. LISTPOSITION lPos = m_pStreamList->GetHeadPosition();
  439. while (lPos)
  440. {
  441.     CHXAudioStream* pStream =
  442. (CHXAudioStream* ) m_pStreamList->GetAt(lPos);
  443.     if (pStream == pFromStream ||
  444. pStream == pToStream)
  445.     {
  446. /* RemoveAt returns the next position in the list.
  447.  * DO NOT use GetNext if you remove a node.
  448.  */
  449. lPos = m_pStreamList->RemoveAt(lPos);
  450.     }
  451.     else
  452.     {
  453. m_pStreamList->GetNext(lPos);
  454.     }
  455. }
  456. m_pStreamList->AddTail((void*) pToStream);
  457. m_pStreamList->AddTail((void*) pFromStream);
  458.     }
  459. #endif /* HELIX_FEATURE_CROSSFADE */
  460.     return HXR_OK;
  461. }
  462. /************************************************************************
  463.  *  Method:
  464.  * CHXAudioPlayer::Init
  465.  * Purpose:
  466.  * Initialize the Audio Player object called by rmaplayer.
  467.  */
  468. HX_RESULT CHXAudioPlayer::Init
  469. (
  470.     IUnknown* pContext
  471. )
  472. {
  473.     if (!pContext)
  474. return HXR_INVALID_PARAMETER;
  475.     m_pContext = pContext;
  476.     m_pContext->AddRef();
  477.     if (HXR_OK != pContext->QueryInterface(IID_IHXScheduler,
  478. (void **) &m_pScheduler))
  479.     {
  480. return HXR_INVALID_PARAMETER;
  481.     }
  482.     if (HXR_OK != pContext->QueryInterface(IID_IHXAudioPlayerResponse,
  483. (void **) &m_pPlayerResponse))
  484.     {
  485. return HXR_INVALID_PARAMETER;
  486.     }
  487. #if defined( HELIX_FEATURE_PREFERENCES )
  488.     m_pContext->QueryInterface(IID_IHXPreferences, (void**) &m_pPreferences);
  489. #endif
  490.     return HXR_OK;
  491. }
  492. /************************************************************************
  493.  *  Method:
  494.  * CHXAudioPlayer::_Init
  495.  * Purpose:
  496.  * Create internal lists, etc. Called by Audio Session
  497.  * CreateAudioPlayer() method.
  498.  */
  499. HX_RESULT CHXAudioPlayer::InitializeStructures()
  500. {
  501.     HX_RESULT theErr = HXR_OK;
  502.     // Create the Stream list.
  503.     // Create the Post process hook list.
  504.     // Create the Stream response list.
  505.     m_pStreamList = new CHXSimpleList;
  506. #if defined(HELIX_FEATURE_AUDIO_POSTMIXHOOK)
  507.     m_pPMixHookList = new CHXSimpleList;
  508. #endif /* HELIX_FEATURE_AUDIO_POSTMIXHOOK */
  509.     m_pStreamRespList = new CHXSimpleList;
  510.     if ( !m_pStreamList || !m_pStreamList->IsPtrListValid())
  511. theErr = HXR_OUTOFMEMORY;
  512. #if defined(HELIX_FEATURE_AUDIO_POSTMIXHOOK)
  513.     if ( !m_pPMixHookList || !m_pPMixHookList->IsPtrListValid())
  514. theErr = HXR_OUTOFMEMORY;
  515. #endif /* HELIX_FEATURE_AUDIO_POSTMIXHOOK */
  516.     if ( !m_pStreamRespList || !m_pStreamRespList->IsPtrListValid())
  517. theErr = HXR_OUTOFMEMORY;
  518. #if defined(HELIX_FEATURE_VOLUME)
  519.     if( !theErr )
  520.     {
  521.         m_pPlayerVolume = (IHXVolume*)new CHXVolume;
  522.         if( m_pPlayerVolume )
  523.         {
  524.             m_pPlayerVolume->AddRef();
  525.             m_pPlayerVolume->AddAdviseSink(this);
  526.             //Start off with the volume at max.
  527.             m_pPlayerVolume->SetVolume(HX_MAX_VOLUME);
  528.         }
  529.         else
  530.         {
  531.             theErr = HXR_OUTOFMEMORY;
  532.         }
  533.     }
  534. #endif /* HELIX_FEATURE_VOLUME */
  535.     return ( theErr );
  536. }
  537. /************************************************************************
  538.  *  Method:
  539.  * CHXAudioPlayer::GetFormat
  540.  * Purpose:
  541.  * Return the player's device format which was determined in
  542.  * InitPlayer() and is based on all the streams.
  543.  */
  544. void CHXAudioPlayer::GetFormat
  545. (
  546. HXAudioFormat*   pAudioFormat
  547. )
  548. {
  549.     memcpy(pAudioFormat, &m_PlayerFmt, sizeof( HXAudioFormat) );
  550. }
  551. /* ***********************************************************************
  552.  *  Method:
  553.  *      CHXAudioPlayer::GetAudioPrefs
  554.  *  Purpose:
  555.  * Get audio related preferences.
  556.  */
  557. HX_RESULT CHXAudioPlayer::GetAudioPrefs()
  558. {
  559.     IHXBuffer* pBuffer = NULL;
  560.     IHXPreferences* pPreferences = 0;
  561. #if defined(HELIX_FEATURE_PREFERENCES)
  562.     /* Reason we query for Preferences here and not at Init() is because
  563.      * Preferences may have been overwritten in HXPlayer by SetupClient()
  564.      * call by upper level client and this happens AFTER CHXAudioPlayer::Init()
  565.      * is called
  566.      */
  567.     if (!m_pContext)
  568.     {
  569. return HXR_INVALID_PARAMETER;
  570.     }
  571.     m_pContext->QueryInterface(IID_IHXPreferences, (void**) &pPreferences);
  572.     //  What is the pref for this?
  573.     if( pPreferences )
  574.     {
  575.         if (pPreferences->ReadPref("SamplingRate", pBuffer) == HXR_OK)
  576.         {
  577.             m_bPrefUse11khz = (11025 == ::atol((const char*) pBuffer->GetBuffer()));
  578.             pBuffer->Release();
  579.             pBuffer = 0;
  580.         }
  581. ReadPrefINT16(pPreferences, "BitsPerSample", m_uPrefBitsPerSample);
  582. ReadPrefINT16(pPreferences, "Quality", m_uPrefAudioQuality);
  583.         /* hmmm... Looks like the client override default Preferences implementation*/
  584.         if (m_pPreferences != pPreferences)
  585.         {
  586.             if (m_pPreferences)
  587.             {
  588.                 m_pPreferences->Release();
  589.             }
  590.             m_pPreferences = pPreferences;
  591.             m_pPreferences->AddRef();
  592.         }
  593.         pPreferences->Release();
  594.     }
  595. #endif /* HELIX_FEATURE_PREFERENCES */
  596.     return HXR_OK;
  597. }
  598. /************************************************************************
  599.  *  Method:
  600.  * IHXAudioPlay::CreateAudioStream
  601.  * Purpose:
  602.  *       The renderer calls this to create a unique audio stream with its
  603.  *  unique audio format.
  604.  */
  605. STDMETHODIMP CHXAudioPlayer::CreateAudioStream
  606. (
  607.     IHXAudioStream**   pAudioStream
  608. )
  609. {
  610.    HX_RESULT theErr = HXR_OK;
  611.     // Create a new IRMA audio stream
  612.     *pAudioStream = 0;
  613.     *pAudioStream = (IHXAudioStream*) new CHXAudioStream(this, m_pContext);
  614.     if ( !*pAudioStream )
  615.     {
  616. theErr = HXR_OUTOFMEMORY;
  617.     }
  618.     // Add audio stream to my list
  619.     if (!theErr)
  620.     {
  621. theErr = _CreateAudioStream(pAudioStream);
  622.     }
  623.     return theErr;
  624. }
  625. HX_RESULT
  626. CHXAudioPlayer::_CreateAudioStream(IHXAudioStream** pAudioStream)
  627. {
  628.     (*pAudioStream)->AddRef();   // once for user
  629.     (*pAudioStream)->AddRef();   // once for me
  630.     // Add to the stream list.
  631.     m_pStreamList->AddTail((void*) *pAudioStream);
  632.     ((CHXAudioStream*)(*pAudioStream))->SetLive(m_bIsLive);
  633.     m_Owner->CheckIfLastNMilliSecsToBeStored();
  634.     /* Already initialized with no streams?*/
  635.     if (m_bInited && !m_bHasStreams)
  636.     {
  637. /* If we are already initialized and there were no audio streams before
  638.  * initialization, we must be using our fake timer to send time syncs
  639.  * This needs to change now to get time syncs from the audio device
  640.  */
  641. ((CHXAudioStream*)(*pAudioStream))->SetupToBeDone();
  642. return HXR_OK;
  643.     }
  644.     m_bHasStreams = TRUE;
  645.     /* If we are already initialized, it means CreateAudioStream was
  646.      * called in the midst of the presentation. In this case, we already know
  647.      * the granularity and the Device format and thus call Setup right away
  648.      */
  649.     if ((*pAudioStream) && m_bInited)
  650.     {
  651. ((CHXAudioStream*)(*pAudioStream))->Setup( &m_DeviceFmt, m_ulGranularity );
  652.     }
  653.     return HXR_OK;
  654. }
  655. HX_RESULT
  656. CHXAudioPlayer::SetSoundLevel(CHXSimpleList* pAudioStreamList, UINT16 uSoundLevel, BOOL bReflushAudioDevice)
  657. {
  658.     HX_RESULT rc = HXR_OK;
  659.     IHXVolume* pStreamVolume = NULL;
  660.     if (pAudioStreamList && !pAudioStreamList->IsEmpty())
  661.     {
  662. CHXSimpleList::Iterator lIter = pAudioStreamList->Begin();
  663. for (; lIter != pAudioStreamList->End(); ++lIter)
  664. {
  665.     CHXAudioStream* pAudioStream = (CHXAudioStream*) (*lIter);
  666.     pStreamVolume = pAudioStream->GetAudioVolume();
  667.     if (pStreamVolume)
  668.     {
  669. pStreamVolume->SetVolume(uSoundLevel);
  670.     }
  671.     HX_RELEASE(pStreamVolume);
  672. }
  673. if (bReflushAudioDevice)
  674. {
  675.     AudioStreamStateChanged(E_PLAYING);
  676. }
  677.     }
  678.     return rc;
  679. }
  680. HX_RESULT CHXAudioPlayer::ManageAudioStreams( CHXSimpleList* pStreamList,
  681.                                               STREAM_ACTION  what,
  682.                                               UINT32 ulTime )
  683. {
  684.     HX_RESULT    rc  = HXR_OK;
  685.     LISTPOSITION pos = 0;
  686.     if(pStreamList && !pStreamList->IsEmpty())
  687.     {
  688. CHXSimpleList::Iterator it = pStreamList->Begin();
  689. while( it != pStreamList->End() )
  690. {
  691.     CHXAudioStream* pAudioStream = (CHXAudioStream*) (*it);
  692.             switch(what)
  693.             {
  694.                case STR_STOP:
  695.                    pAudioStream->Stop();
  696.                    break;
  697.                case STR_SEEK:
  698.                    pAudioStream->Seek(ulTime);
  699.                    break;
  700.                case STR_RESUME:
  701.                    pAudioStream->Resume(FALSE);
  702.                    break;
  703.                case STR_PAUSE:
  704.                    pAudioStream->Pause(FALSE);
  705.                    break;
  706.                case STR_SETHINT:
  707.                    pAudioStream->SetAudioDeviceReflushHint(TRUE);
  708.                    break;
  709.                case STR_REMOVE:
  710.                    pos = m_pStreamList->Find(pAudioStream);
  711.                    if (pos)
  712.                        m_pStreamList->RemoveAt(pos);
  713.                    pAudioStream->Stop();
  714.                    HX_RELEASE(pAudioStream);
  715.                    break;
  716.                default:
  717.                    HX_ASSERT("bad stream action taken"==NULL);
  718.             }
  719.             ++it;
  720. }
  721.         //Post stream iteration actions.
  722.         switch(what)
  723.         {
  724.            case STR_STOP:
  725.                AudioStreamStateChanged(E_STOPPED);
  726.                break;
  727.            case STR_PAUSE:
  728.                AudioStreamStateChanged(E_PAUSED);
  729.                break;
  730.            case STR_SETHINT:
  731.                m_Owner->CheckIfLastNMilliSecsToBeStored();
  732.                break;
  733.            case STR_REMOVE:
  734.                if( 0 == m_pStreamList->GetCount())
  735.                {
  736.                    m_bHasStreams = FALSE;
  737.                    m_Owner->Stop(this, TRUE);
  738.                    m_bInited = FALSE;
  739.                    if(HXR_OK != (rc=Setup(m_ulGranularity)))
  740.                    {
  741.                        IHXErrorMessages* pErrorMessage = NULL;
  742.                        m_pContext->QueryInterface(IID_IHXErrorMessages, (void**) &pErrorMessage);
  743.                        if (pErrorMessage)
  744.                        {
  745.                            pErrorMessage->Report(HXLOG_ERR, rc, 0, NULL, NULL);
  746.                            pErrorMessage->Release();
  747.                        }
  748.                        rc = HXR_OK;
  749.                    }
  750.                    else
  751.                    {
  752.                        rc = ResumeFakeTimeline();
  753.                    }
  754.                }
  755.                else
  756.                {
  757.                    AudioStreamStateChanged(E_STOPPED);
  758.                }
  759.                break;
  760.            default:
  761.                HX_ASSERT("bad stream action taken"==NULL );
  762.         }
  763.     }
  764.     return rc;
  765. }
  766. HX_RESULT
  767. CHXAudioPlayer::AudioStreamStateChanged(EPlayerState eState)
  768. {
  769.     if(!m_Owner->GetDisableMultiPlayPauseSupport())
  770.     {
  771. // we only concern about the state change of audio stream
  772. // while its parent audio player is in playing mode
  773. if (m_eState == E_PLAYING)
  774. {
  775.     switch (eState)
  776.     {
  777.     case E_PLAYING:
  778. m_Owner->RewindSession();
  779. m_Owner->ActualResume();
  780. break;
  781.     case E_PAUSED:
  782.     case E_STOPPED:
  783. m_Owner->RewindSession();
  784. if (NumberOfResumedStreams() > 0 ||
  785.     m_Owner->NumberOfResumedPlayers() > 0)
  786. {
  787.     m_Owner->ActualResume();
  788. }
  789. break;
  790.     default:
  791. break;
  792.     }
  793. }
  794.     }
  795.     return HXR_OK;
  796. }
  797. CHXAudioStream*
  798. CHXAudioPlayer::GetCHXAudioStream(UINT16 uIndex)
  799. {
  800.     LISTPOSITION lp = 0;
  801.     lp = m_pStreamList->FindIndex( (int) uIndex );
  802.     if ( lp )
  803.     {
  804. return (CHXAudioStream*)m_pStreamList->GetAt(lp);
  805.     }
  806.     else
  807.     {
  808.         return NULL;
  809.     }
  810. }
  811. BOOL
  812. CHXAudioPlayer::IsLastNMilliSecsToBeStored()
  813. {
  814.     BOOL bResult = FALSE;
  815.     if (m_bHasStreams)
  816.     {
  817. CHXAudioStream* s = 0;
  818. CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
  819. for (; lIter != m_pStreamList->End(); ++lIter)
  820. {
  821.     s = (CHXAudioStream*) (*lIter);
  822.     if (s->IsAudioDeviceReflushHint())
  823.     {
  824. bResult = TRUE;
  825. break;
  826.     }
  827. }
  828.     }
  829.     return bResult;
  830. }
  831. HX_RESULT
  832. CHXAudioPlayer::ActualAddPostMixHook(IHXAudioHook* pHook,
  833.           const BOOL     bDisableWrite,
  834.      const BOOL     bFinal)
  835. {
  836.     if (!m_pPMixHookList || !pHook)
  837.     {
  838. return HXR_FAILED;
  839.     }
  840. #if defined(HELIX_FEATURE_AUDIO_POSTMIXHOOK)
  841.     /* Check if this one already exists */
  842.     HXAudioHookInfo* h = 0;
  843.     LISTPOSITION lp = 0;
  844.     lp = m_pPMixHookList->GetHeadPosition();
  845.     while( lp )
  846.     {
  847. h = (HXAudioHookInfo*) m_pPMixHookList->GetNext(lp);
  848. if (pHook == h->pHook)
  849. {
  850.     return HXR_FAILED;
  851. }
  852.     }
  853.     h = (HXAudioHookInfo*) new HXAudioHookInfo;
  854.     h->pHook   = pHook;
  855.     h->bDisableWrite = bDisableWrite;
  856.     h->bFinal  = bFinal;
  857.     h->bIgnoreAudioData = FALSE;
  858.     h->bMultiChannelSupport = FALSE;
  859.     IHXValues* pValues = NULL;
  860.     if (pHook && pHook->QueryInterface(IID_IHXValues, (void**) &pValues) == HXR_OK)
  861.     {
  862. UINT32 ulValue = 0;
  863. pValues->GetPropertyULONG32("IgnoreAudioData", ulValue);
  864. h->bIgnoreAudioData = (ulValue == 1);
  865. HX_RELEASE(pValues);
  866.     }
  867.     IHXAudioMultiChannel* pMultiChannel = NULL;
  868.     if (pHook && HXR_OK == pHook->QueryInterface(IID_IHXAudioMultiChannel, (void**) &pMultiChannel))
  869.     {
  870.         h->bMultiChannelSupport = pMultiChannel->GetMultiChannelSupport();
  871.     }
  872.     HX_RELEASE(pMultiChannel);
  873.     if (bDisableWrite)
  874.     {
  875. m_bDisableWrite = bDisableWrite;
  876.     }
  877.     pHook->AddRef();
  878.     // Order list by putting all bFinal == TRUE at end of list.
  879.     if ( m_pPMixHookList->IsEmpty() || !bFinal )
  880.     {
  881. m_pPMixHookList->AddHead((void*) h);
  882.     }
  883.     else
  884.     {
  885. m_pPMixHookList->AddTail((void*) h);
  886.     }
  887.     m_Owner->PostMixHooksUpdated();
  888.     ProcessAudioHook(ACTION_ADD, pHook);
  889.     /* If we are already initialized, send the device format to the
  890.      * post hook
  891.      */
  892.     if (m_bInited)
  893.     {
  894. if (h->bIgnoreAudioData ||
  895.     HXR_OK == ProcessAudioHook(ACTION_CHECK, pHook))
  896. {
  897.     HXAudioFormat audioFmt;
  898.     m_Owner->GetFormat( &audioFmt );
  899.     pHook->OnInit( &audioFmt );
  900. }
  901.     }
  902. #endif /* HELIX_FEATURE_AUDIO_POSTMIXHOOK */
  903.     return HXR_OK;
  904. }
  905. HX_RESULT
  906. CHXAudioPlayer::ActualRemovePostMixHook(IHXAudioHook* pHook)
  907. {
  908.     if (!m_pPMixHookList || !pHook)
  909.     {
  910. return HXR_FAILED;
  911.     }
  912. #if defined(HELIX_FEATURE_AUDIO_POSTMIXHOOK)
  913.     BOOL bCheckForDisableWrite = FALSE;
  914.     BOOL bFound = FALSE;
  915.     HXAudioHookInfo* h = 0;
  916.     LISTPOSITION lp, lastlp;
  917.     lp = lastlp = 0;
  918.     lp = m_pPMixHookList->GetHeadPosition();
  919.     while( lp )
  920.     {
  921. lastlp = lp;
  922. h = (HXAudioHookInfo*) m_pPMixHookList->GetNext(lp);
  923. if ( pHook == h->pHook )
  924. {
  925.     if (h->bDisableWrite)
  926.     {
  927. m_bDisableWrite = FALSE;
  928. bCheckForDisableWrite = TRUE;
  929.     }
  930.     ProcessAudioHook(ACTION_REMOVE, pHook);
  931.     h->pHook->Release();
  932.     delete h;
  933.     h = 0;
  934.     m_pPMixHookList->RemoveAt(lastlp);
  935.     bFound = TRUE;
  936.     break;
  937. }
  938.     }
  939.     if (!bFound)
  940.     {
  941. return HXR_FAILED;
  942.     }
  943.     m_Owner->PostMixHooksUpdated();
  944.     if ( m_pPMixHookList && bCheckForDisableWrite && m_pPMixHookList->GetCount() > 0)
  945.     {
  946. HXAudioHookInfo* h = 0;
  947. LISTPOSITION lp, lastlp;
  948. lp = lastlp = 0;
  949. lp = m_pPMixHookList->GetHeadPosition();
  950.     while( lp )
  951. {
  952.     h = (HXAudioHookInfo*) m_pPMixHookList->GetNext(lp);
  953.     if (h->bDisableWrite)
  954.     {
  955. m_bDisableWrite = TRUE;
  956. break;
  957.     }
  958. }
  959.     }
  960. #endif /* HELIX_FEATURE_AUDIO_POSTMIXHOOK */
  961.     return HXR_OK;
  962. }
  963. /************************************************************************
  964.  *  Method:
  965.  *      CHXAudioPlayer::SetGranularity
  966.  *  Purpose:
  967.  *       The HELIX player object calls this BEFORE starting audio playback
  968.  *       and AFTER all audio streams are created for the renderers.
  969.  */
  970. void CHXAudioPlayer::SetGranularity
  971. (
  972.     const ULONG32  ulGranularity
  973. )
  974. {
  975.     m_ulGranularity = ulGranularity;
  976.     return;
  977. }
  978. /************************************************************************
  979.  *  Method:
  980.  * CHXAudioPlayer::Resume
  981.  * Purpose:
  982.  * Resume audio playback by writing data to the audio device.
  983.  * Open the audio device if it is not opened already.
  984.  */
  985. HX_RESULT CHXAudioPlayer::Resume()
  986. {
  987.     HX_RESULT theErr = HXR_OK;
  988.     if (!m_bInited)
  989. return HXR_NOT_INITIALIZED;
  990.     if ( m_eState == E_PLAYING )
  991.     {
  992. return HXR_OK;
  993.     }
  994.     m_bIsDonePlayback = FALSE;
  995.     m_eState          = E_PLAYING;
  996.     m_bCanBeRewound   = TRUE;
  997.     /* Use Audio Session Object ONLY if there are any audio streams
  998.      * in the presentation
  999.      */
  1000.     if (m_bHasStreams)
  1001.     {
  1002. CHXAudioStream* s = 0;
  1003. CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
  1004. for (; lIter != m_pStreamList->End(); ++lIter)
  1005. {
  1006.     s = (CHXAudioStream*) (*lIter);
  1007.     if ( s )
  1008. s->Resume(TRUE);
  1009. }
  1010. // This is the audio device playback time that corresponds to
  1011. // when this audio player resumed.
  1012. m_ulADresumeTime = m_Owner->GetCurrentPlayBackTime();
  1013. // This is this player's start time within its timeline. This is
  1014. // modified when the player is seeked or resumed.
  1015. m_ulAPstartTime = m_ulAPplaybackTime;
  1016. UpdateStreamLastWriteTime();
  1017. // Resume the audio device playback
  1018. if ( !theErr )
  1019.     theErr = m_Owner->Resume(this);
  1020.     }
  1021.     else
  1022.     {
  1023. theErr = ResumeFakeTimeline();
  1024. /* Send time 0 at first Resume */
  1025. if (!theErr && m_bIsFirstResume)
  1026. {
  1027.     m_bIsFirstResume = FALSE;
  1028.     OnTimeSync(m_ulIncreasingTimer);
  1029. }
  1030.     }
  1031.     m_bIsResumed = TRUE;
  1032.     return ( !theErr ) ? HXR_OK : HXR_FAILED;
  1033. }
  1034. /************************************************************************
  1035.  *  Method:
  1036.  * CHXAudioPlayer::Pause
  1037.  * Purpose:
  1038.  * The player object calls this function to pause audio playback.
  1039.  */
  1040. HX_RESULT CHXAudioPlayer::Pause()
  1041. {
  1042.     if (m_eState == E_PAUSED)
  1043.     {
  1044. return HXR_OK;
  1045.     }
  1046.     m_eState = E_PAUSED;
  1047.     if (m_bHasStreams)
  1048.     {
  1049. CHXAudioStream* s = 0;
  1050. CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
  1051. for (; lIter != m_pStreamList->End(); ++lIter)
  1052. {
  1053.     s = (CHXAudioStream*) (*lIter);
  1054.     if ( s )
  1055. s->Pause(TRUE);
  1056. }
  1057. m_Owner->Pause(this);
  1058.     }
  1059.     else
  1060.     {
  1061. StopFakeTimeline();
  1062.     }
  1063.     m_bCanBeRewound = FALSE;
  1064.     return HXR_OK;
  1065. }
  1066. /************************************************************************
  1067.  *  Method:
  1068.  * CHXAudioPlay::Stop
  1069.  * Purpose:
  1070.  *      The player object calls this function to stop audio playback.
  1071.  * If bFlush is TRUE, flush any data in the audio device.
  1072.  */
  1073. HX_RESULT CHXAudioPlayer::Stop
  1074. (
  1075.     const BOOL bFlush
  1076. )
  1077. {
  1078.     m_eState = E_STOPPED;
  1079.     m_ulAPstartTime  = 0;
  1080.     if (m_bHasStreams)
  1081.     {
  1082. CHXAudioStream* s = 0;
  1083. CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
  1084. for (; lIter != m_pStreamList->End(); ++lIter)
  1085. {
  1086.     s = (CHXAudioStream*) (*lIter);
  1087.     if ( s )
  1088. s->Stop();
  1089. }
  1090. m_Owner->Stop(this, bFlush);
  1091.     }
  1092.     else
  1093.     {
  1094. StopFakeTimeline();
  1095.     }
  1096.     ResetPlayer();
  1097.     return HXR_OK;
  1098. }
  1099. /************************************************************************
  1100.  *  Method:
  1101.  * CHXAudioPlayer::Seek
  1102.  * Purpose:
  1103.  * The player object calls this function to seek audio playback to
  1104.  * the time (in milliseconds) given.
  1105.  */
  1106. HX_RESULT CHXAudioPlayer::Seek
  1107. (
  1108. const  UINT32 ulSeekTime
  1109. )
  1110. {
  1111.     /* always remember this seek time.. even though there may not be any streams
  1112.      * yet for this player. This is because the streams may be created later and
  1113.      * we need to correctly apply the seek time to get the accurate time.
  1114.      */
  1115.     m_ulAPstartTime = m_ulAPplaybackTime = ulSeekTime; // current start time for this player
  1116.     m_llLastWriteTime = (INT64) ulSeekTime;
  1117.     if (m_bHasStreams)
  1118.     {
  1119. // Make each stream seek, too, since they own the resampling buffers.
  1120. CHXAudioStream* s = 0;
  1121. CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
  1122. for (; lIter != m_pStreamList->End(); ++lIter)
  1123. {
  1124.     s = (CHXAudioStream*) (*lIter);
  1125.     if ( s )
  1126. s->Seek(ulSeekTime);
  1127. }
  1128. m_Owner->Seek( this, ulSeekTime );
  1129. m_ulADresumeTime = m_Owner->GetCurrentPlayBackTime();
  1130.     }
  1131.     else
  1132.     {
  1133. StopFakeTimeline();
  1134. m_bIsFirstResume    = TRUE;
  1135.     }
  1136.     m_ulCurrentTime     = ulSeekTime;
  1137.     m_ulLastCurrentTimeReturned = m_ulCurrentTime;
  1138.     m_bTimeReturned     = FALSE;
  1139.     m_bHasDataInAudioDevice = FALSE;
  1140.     return HXR_OK;
  1141. }
  1142. void CHXAudioPlayer::ResetPlayer(void)
  1143. {
  1144.     m_bInited = FALSE;
  1145.     m_bHasStreams = FALSE;
  1146.     m_bIsFirstResume = TRUE;
  1147.     m_ulAPstartTime = 0;
  1148.     m_ulAPplaybackTime = 0;
  1149.     m_ulADresumeTime = 0;
  1150.     m_ulCurrentTime = 0;
  1151.     m_ulLastCurrentTimeReturned = 0;
  1152.     m_ulLastDeviceTimeAdjusted = 0;
  1153.     m_bTimeReturned = FALSE;
  1154.     m_bIsLive = FALSE;
  1155.     m_bIsResumed = FALSE;
  1156.     m_bIsDonePlayback = TRUE;
  1157.     m_llLastWriteTime = 0;
  1158.     m_bCanBeRewound = FALSE;
  1159.     m_bHasDataInAudioDevice = FALSE;
  1160.     // Delete all streams.  Remove all list items.
  1161.     if ( m_pStreamList )
  1162.     {
  1163. CHXAudioStream* pAudioStream = 0;
  1164. while(!m_pStreamList->IsEmpty())
  1165. {
  1166.     pAudioStream = (CHXAudioStream*) m_pStreamList->RemoveHead();
  1167.     pAudioStream->ResetStream();
  1168.     pAudioStream->Release();
  1169. }
  1170.     }
  1171.     /* We do not remove post mix hooks any more */
  1172.     /* We do not remove Stream Response Objects any more */
  1173.     /* Default value of Player format */
  1174.     m_PlayerFmt.uChannels = 2;
  1175.     m_PlayerFmt.uBitsPerSample = 16;
  1176.     m_PlayerFmt.ulSamplesPerSec = 16000;
  1177.     m_PlayerFmt.uMaxBlockSize = 64000;
  1178.     m_ulLastFakeCallbackTime = 0;
  1179.     StopFakeTimeline();
  1180. }
  1181. /************************************************************************
  1182.  *  Method:
  1183.  * CHXAudioPlayer::SetupStreams
  1184.  * Purpose:
  1185.  * Tell each stream about the audio device format so
  1186.  * they can setup their resamplers and buffer.
  1187.  */
  1188. void CHXAudioPlayer::SetupStreams(void)
  1189. {
  1190.     // Get audio device format
  1191.     m_Owner->GetFormat(&m_DeviceFmt);
  1192.     // Calculate bytes per gran
  1193.     m_ulBytesPerGran = (ULONG32)
  1194.                 (((m_DeviceFmt.uChannels * ((m_DeviceFmt.uBitsPerSample==8)?1:2) *  m_DeviceFmt.ulSamplesPerSec)
  1195.                                 / 1000.0) * m_ulGranularity);
  1196.     // Make sure that number of bytes per granularity is an even number.
  1197.     if ( (m_ulBytesPerGran % 2) != 0 )
  1198.         m_ulBytesPerGran++;
  1199.     /* Don't we have to calculate granularity again if we adjust
  1200.      * for even byte boundary - XXX Rahul 06/15/97
  1201.      */
  1202.     // Notify each stream
  1203.     CHXAudioStream* s = 0;
  1204.     CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
  1205.     for (; lIter != m_pStreamList->End(); ++lIter)
  1206.     {
  1207. s = (CHXAudioStream*) (*lIter);
  1208. if ( s )
  1209. {
  1210.     s->Setup( &m_DeviceFmt, m_ulGranularity );
  1211. }
  1212.     }
  1213. }
  1214. /************************************************************************
  1215.  *  Method:
  1216.  * CHXAudioPlayer::OnTimeSync
  1217.  * Purpose:
  1218.  */
  1219. HX_RESULT CHXAudioPlayer::OnTimeSync(ULONG32 ulCurrentTime)
  1220. {
  1221.     HX_RESULT theErr = HXR_OK;
  1222. #ifdef _MACINTOSH
  1223.     if (InterlockedIncrement(&gTIMELINE_MUTEX) > 1)
  1224.     {
  1225.        InterlockedDecrement(&gTIMELINE_MUTEX);
  1226.        return;
  1227.     }
  1228.     InterlockedDecrement(&gTIMELINE_MUTEX);
  1229. #endif
  1230.     if (m_bHasStreams)
  1231.     {
  1232. ULONG32 ulADplaybackTime;
  1233. ulADplaybackTime = m_Owner->GetCurrentPlayBackTime();
  1234. m_ulAPplaybackTime = (ulADplaybackTime - m_ulADresumeTime) +
  1235.       m_ulAPstartTime;
  1236.     }
  1237.     else
  1238.     {
  1239. m_ulAPplaybackTime  = ulCurrentTime;
  1240.     }
  1241.     m_ulCurrentTime = m_ulAPplaybackTime ;
  1242.     AdjustForRealAudio();
  1243.     // Here we need to fudge the actual time for this player
  1244.     // For now we support only one player/timeline
  1245.     if (m_pPlayerResponse)
  1246.     {
  1247. // The current playback time of any player is the difference
  1248. // of the current audio device playback time minus the audio
  1249. // device time when this player started (resumed) playback
  1250. // plus the initial start time of playback within this player's
  1251. // timeline (usually 0 but can be something else esp. after a
  1252. // seek).
  1253. theErr = m_pPlayerResponse->OnTimeSync(m_ulCurrentTime);
  1254.     }
  1255.     return theErr;
  1256. }
  1257. /************************************************************************
  1258.  *  Method:
  1259.  * CHXAudioPlayer::Setup
  1260.  * Purpose:
  1261.  * This is called after AS receives format and stream info
  1262.  * from the renderers AND before packets are received from
  1263.  * the renderer.
  1264.  */
  1265. HX_RESULT CHXAudioPlayer::Setup( ULONG32 ulGranularity)
  1266. {
  1267.     HX_RESULT theErr = HXR_OK;
  1268.     if (m_bInited)
  1269. return HXR_OK;
  1270.     /* Always write 100ms audio blocks for now. */
  1271.     m_ulGranularity = MAXIMUM_AUDIO_GRANULARITY; //ulGranularity;
  1272.     /* We do not go below MINIMUM_AUDIO_GRANULARITY. This will not affect
  1273.      * sending timesyncs at this lower granularity since HXPlayer object
  1274.      * uses the scheduler to send individual timesyncs anyway
  1275.      */
  1276.     if (m_ulGranularity < MINIMUM_AUDIO_GRANULARITY)
  1277.     {
  1278. m_ulGranularity = MINIMUM_AUDIO_GRANULARITY;
  1279.     }
  1280.     else if (m_ulGranularity > MAXIMUM_AUDIO_GRANULARITY)
  1281.     {
  1282. m_ulGranularity = MAXIMUM_AUDIO_GRANULARITY;
  1283.     }
  1284.     if (!m_bHasStreams)
  1285.     {
  1286. m_bInited = TRUE;
  1287. return HXR_OK;
  1288.     }
  1289.     /* If this is the second player, session object may overide
  1290.      * the granularity value.
  1291.      */
  1292.     m_ulGranularity = m_Owner->SetGranularity(m_ulGranularity);
  1293.     // Determine this player's audio format parameters based on
  1294.     // the mixer channels attributes supplied in RegisterRenderer.
  1295.     //
  1296.     // 1. Spin thru the list of registered streams and
  1297.     //    determine the desired audio device parameters.
  1298.     // 2. Check the audio format with the audio device.
  1299.     //
  1300.     CHXAudioStream* pAudioStream = 0;
  1301.     ULONG32 maxSamplesPerSec = 8000;
  1302.     ULONG32 minSamplesPerSec = 44100;
  1303.     BOOL    bFirst = TRUE;
  1304.     UINT16  maxChannels = 1;
  1305.     UINT16  maxBlocksize = 0;
  1306.     UINT16  maxBitsPerSample = 0;
  1307.     HXAudioFormat audioFmt;
  1308.     theErr = GetAudioPrefs();
  1309.     if (!theErr && m_pStreamList->GetCount() > 0)
  1310.     {
  1311. CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
  1312. for (;lIter != m_pStreamList->End(); ++lIter)
  1313. {
  1314.     pAudioStream = (CHXAudioStream*) (*lIter); //m_pStreamList->GetNext(lp);
  1315.     if (!pAudioStream->IsAudioFormatKnown())
  1316.     {
  1317. continue;
  1318.     }
  1319.     pAudioStream->GetFormat( &audioFmt );
  1320.     if (bFirst)
  1321.     {
  1322. bFirst = FALSE;
  1323. maxSamplesPerSec    = audioFmt.ulSamplesPerSec;
  1324. minSamplesPerSec    = audioFmt.ulSamplesPerSec;
  1325. maxChannels     = audioFmt.uChannels;
  1326. maxBlocksize     = audioFmt.uMaxBlockSize;
  1327. maxBitsPerSample    = audioFmt.uBitsPerSample;
  1328.     }
  1329.     else
  1330.     {
  1331. //
  1332. // NOTE: upsampling typically costs more CPU than downsampling
  1333. if ( audioFmt.ulSamplesPerSec > maxSamplesPerSec )
  1334.     maxSamplesPerSec = audioFmt.ulSamplesPerSec;
  1335. if ( audioFmt.ulSamplesPerSec < minSamplesPerSec)
  1336.     minSamplesPerSec = audioFmt.ulSamplesPerSec;
  1337. //
  1338. // NOTE: converting mono to stereo and vice versa cost about the
  1339. // same in CPU usage.
  1340. if ( audioFmt.uChannels > maxChannels)
  1341.     maxChannels = audioFmt.uChannels;
  1342. // Get max block size.
  1343. if ( audioFmt.uMaxBlockSize > maxBlocksize )
  1344.     maxBlocksize = audioFmt.uMaxBlockSize;
  1345. // Get max sample width.
  1346. if ( audioFmt.uBitsPerSample > maxBitsPerSample )
  1347.     maxBitsPerSample = audioFmt.uBitsPerSample;
  1348.     }
  1349. }
  1350. // Set the audio format for this Player.
  1351. m_PlayerFmt.uMaxBlockSize  = maxBlocksize;
  1352. m_PlayerFmt.uChannels = maxChannels;
  1353. m_PlayerFmt.uBitsPerSample  = maxBitsPerSample;
  1354. // If user wants upsampling
  1355. if ( m_uPrefAudioQuality > 2 )
  1356.     m_PlayerFmt.ulSamplesPerSec = maxSamplesPerSec;
  1357. else
  1358.     m_PlayerFmt.ulSamplesPerSec = minSamplesPerSec;
  1359.     }
  1360.     if (m_bPrefUse11khz)
  1361.     {
  1362. m_PlayerFmt.ulSamplesPerSec = 11025;
  1363.     }
  1364.     // Do audio session setup. (e.g., determine device audio
  1365.     // format, etc.
  1366.     if ( !theErr )
  1367. theErr = m_Owner->Setup( m_bHasStreams );
  1368.     // Now let all streams know the final audio format so they
  1369.     // can resample to this format.
  1370.     if ( !theErr )
  1371.     {
  1372. SetupStreams();
  1373.     }
  1374.     // if audio device is failed to initialized, we
  1375.     // will keep the video playing if this is not audio only source
  1376.     else if (!IsAudioOnlyTrue())
  1377.     {
  1378. m_bHasStreams = FALSE;
  1379. m_bInited = TRUE;
  1380. return HXR_OK;
  1381.     }
  1382.     // Let all stream response know total number of streams.
  1383.     if (!theErr && m_pStreamRespList)
  1384.     {
  1385. IHXAudioStreamInfoResponse* pAudioStreamInfoResponse = 0;
  1386. CHXSimpleList::Iterator lIter = m_pStreamRespList->Begin();
  1387. for (; lIter != m_pStreamRespList->End(); ++lIter)
  1388.   {
  1389.     pAudioStreamInfoResponse = (IHXAudioStreamInfoResponse*) (*lIter);
  1390.     CHXSimpleList::Iterator lIter2 = m_pStreamList->Begin();
  1391.     for (; lIter2 != m_pStreamList->End(); ++lIter2)
  1392.       {
  1393. CHXAudioStream* pStream = (CHXAudioStream*) (*lIter2);
  1394. /* Only if a stream is initialized, send it to
  1395.  * Response object. If not, we will send it when it
  1396.  * gets initialized (in StreamInitialized() call)
  1397.  */
  1398. if (pStream->IsInitialized())
  1399. {
  1400.     pAudioStreamInfoResponse->OnStream(pStream);
  1401. }
  1402.     }
  1403. }
  1404.     }
  1405.     // All renderers should have checked in by now!
  1406.     // Call post mix process hooks in list and provide the audio format.
  1407.     if (!theErr && m_pPMixHookList)
  1408.     {
  1409. HXAudioFormat audioFmt;
  1410. m_Owner->GetFormat( &audioFmt );
  1411. HXAudioHookInfo* pPMixHookInfo = 0;
  1412. CHXSimpleList::Iterator lIter = m_pPMixHookList->Begin();
  1413. for (; lIter != m_pPMixHookList->End(); ++lIter)
  1414. {
  1415.     pPMixHookInfo = (HXAudioHookInfo*) (*lIter);
  1416.     if (pPMixHookInfo->bIgnoreAudioData ||
  1417. HXR_OK == ProcessAudioHook(ACTION_CHECK, pPMixHookInfo->pHook))
  1418.     {
  1419. pPMixHookInfo->pHook->OnInit( &audioFmt );
  1420.     }
  1421. }
  1422.     }
  1423.     if (!theErr)
  1424.     {
  1425.      m_bInited = TRUE;
  1426. /* Only change the state to initialized if we were in a stopped
  1427.  * state earlier. It is possible to be in Playing state and be still
  1428.  * in this function. This will happen if we have started the
  1429.  * timeline as a fake timeline and later an audio stream joins the
  1430.  * presentation thereby converting fake to audio timeline
  1431.  * (delayed audio source in SMIL playback)
  1432.  */
  1433. if (m_eState == E_STOPPED)
  1434. {
  1435.     m_eState = E_INITIALIZED;
  1436. }
  1437.     }
  1438.     return theErr;
  1439. }
  1440. ULONG32 CHXAudioPlayer::GetCurrentPlayBackTime(void)
  1441. {
  1442.     if (m_eState != E_PLAYING)
  1443.     {
  1444. return m_ulCurrentTime;
  1445.     }
  1446.     // The current playback time of any player is the difference
  1447.     // of the current audio device playback time minus the audio
  1448.     // device time when this player started (resumed) playback
  1449.     // plus the initial start time of playback within this player's
  1450.     // timeline (usually 0 but can be something else esp. after a
  1451.     // seek).
  1452.     if (!m_bHasStreams)
  1453.     {
  1454. ULONG32 ulCurrentTime = HX_GET_TICKCOUNT();
  1455. m_ulIncreasingTimer     += CALCULATE_ELAPSED_TICKS(m_ulLastFakeCallbackTime, ulCurrentTime);
  1456. m_ulLastFakeCallbackTime    = ulCurrentTime;
  1457. m_ulCurrentTime     = m_ulIncreasingTimer;
  1458.     }
  1459.     else
  1460.     {
  1461. m_ulCurrentTime = (m_Owner->GetCurrentPlayBackTime() -
  1462.     m_ulADresumeTime) + m_ulAPstartTime;
  1463.     }
  1464.     m_ulAPplaybackTime = m_ulCurrentTime;
  1465.     AdjustForRealAudio();
  1466.     return m_ulCurrentTime;
  1467. }
  1468. /************************************************************************
  1469.  *  Method:
  1470.  * CHXAudioPlayer::GetStreamCount
  1471.  * Purpose:
  1472.  * Get the number of streams associated with this player.
  1473.  */
  1474. UINT16 CHXAudioPlayer::GetStreamCount()
  1475. {
  1476.     return m_pStreamList->GetCount();
  1477. }
  1478. /************************************************************************
  1479.  *  Method:
  1480.  * CHXAudioPlayer::SetStreamInfoResponse
  1481.  * Purpose:
  1482.  * Add the stream info response interface to our list.
  1483.  */
  1484. STDMETHODIMP CHXAudioPlayer::SetStreamInfoResponse
  1485. (
  1486.     IHXAudioStreamInfoResponse*    pResponse
  1487. )
  1488. {
  1489.     if (!pResponse || !m_pStreamRespList)
  1490.     {
  1491. return HXR_FAILED;
  1492.     }
  1493.     /* Add to the stream response list */
  1494.     LISTPOSITION lPos = m_pStreamRespList->Find(pResponse);
  1495.     if (lPos)
  1496.     {
  1497. return HXR_FAILED;
  1498.     }
  1499.     m_pStreamRespList->AddTail((void*) pResponse);
  1500.     pResponse->AddRef();         // Released in destructor
  1501.     return HXR_OK;
  1502. }
  1503. /************************************************************************
  1504.  *  Method:
  1505.  *  CHXAudioPlayer::RemoveStreamInfoResponse
  1506.  *  Purpose:
  1507.  *  Remove stream info response that was added earlier
  1508.  */
  1509. STDMETHODIMP CHXAudioPlayer::RemoveStreamInfoResponse
  1510. (
  1511.     IHXAudioStreamInfoResponse*    pResponse
  1512. )
  1513. {
  1514.     /* Add to the stream response list */
  1515.     if (pResponse && m_pStreamRespList)
  1516.     {
  1517. LISTPOSITION lPos = m_pStreamRespList->Find(pResponse);
  1518. if (lPos)
  1519. {
  1520.     m_pStreamRespList->RemoveAt(lPos);
  1521.     pResponse->Release();         // Released in destructor
  1522.     return HXR_OK;
  1523. }
  1524.     }
  1525.     return HXR_FAILED;
  1526. }
  1527. UINT16
  1528. CHXAudioPlayer::NumberOfResumedStreams(void)
  1529. {
  1530.     UINT16 uNumActive = 0;
  1531.     if (m_pStreamList && m_pStreamList->GetCount() > 0)
  1532.     {
  1533. CHXAudioStream* pStream = 0;
  1534. CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
  1535. for (; lIter != m_pStreamList->End(); ++lIter)
  1536. {
  1537.     pStream = (CHXAudioStream*) (*lIter);
  1538.     if (pStream->GetState() == E_PLAYING)
  1539.     {
  1540. uNumActive++;
  1541.     }
  1542. }
  1543.     }
  1544.     return uNumActive;
  1545. }
  1546. void CHXAudioPlayer::StreamInitialized(CHXAudioStream* pAudioStream)
  1547. {
  1548.     /* If we are already initialized, it means this stream was added mid-
  1549.      * presentation and gogt initialized later. In this case, report arrival
  1550.      * of this stream to all StreamInfoResponse objects registered with the
  1551.      * player
  1552.      */
  1553.     if (m_pStreamRespList && m_bInited)
  1554.     {
  1555. IHXAudioStreamInfoResponse* pAudioStreamInfoResponse = 0;
  1556. CHXSimpleList::Iterator lIter = m_pStreamRespList->Begin();
  1557. for (; lIter != m_pStreamRespList->End(); ++lIter)
  1558.   {
  1559.     pAudioStreamInfoResponse = (IHXAudioStreamInfoResponse*) (*lIter);
  1560.     pAudioStreamInfoResponse->OnStream(pAudioStream);
  1561. }
  1562.     }
  1563.     m_bHasStreams = TRUE;
  1564. }
  1565. ULONG32 CHXAudioPlayer::GetInitialPushdown(BOOL bAtStart /* = FALSE*/)
  1566. {
  1567.     /* If there are any audio streams, initial pushdown is session's
  1568.      * initial pushdown
  1569.      */
  1570.     if (m_pStreamList->GetCount() > 0)
  1571.     {
  1572.         ULONG32 ulRet = 
  1573.             m_Owner->GetInitialPushdown(bAtStart) + m_ulGranularity;
  1574. #ifdef HELIX_FEATURE_MIN_HEAP
  1575.         // The MIN_HEAP code seems to need 1 extra block's worth
  1576.         // of data to avoid rebuffers during playback. This
  1577.         // is a low impact temporary solution that fixes the problem.
  1578.         ulRet += m_ulGranularity;
  1579. #endif /* HELIX_FEATURE_MIN_HEAP */
  1580.         return ulRet;
  1581.     }
  1582.     else
  1583.     {
  1584. return 0;
  1585.     }
  1586. }
  1587. /************************************************************************
  1588.  *  Method:
  1589.  *      CHXAudioPlayer::OnTimerCallback
  1590.  *  Purpose:
  1591.  *      Timer callback when implementing fake timeline.
  1592.  */
  1593. void CHXAudioPlayer::OnTimerCallback()
  1594. {
  1595.     ULONG32 ulCurrentTime = HX_GET_TICKCOUNT();
  1596.     m_ulIncreasingTimer += CALCULATE_ELAPSED_TICKS(m_ulLastFakeCallbackTime, ulCurrentTime);
  1597.     m_ulLastFakeCallbackTime    = ulCurrentTime;
  1598.     OnTimeSync(m_ulIncreasingTimer);
  1599.     /* A call to timesync may result in stopping
  1600.      * playback and we do not want to have any more
  1601.      * time syncs.
  1602.      */
  1603.     /* Put this back in the scheduler.
  1604.      */
  1605.     if (m_bInited && m_eState == E_PLAYING && !m_ulCallbackID)
  1606.     {
  1607. *m_pFakeAudioCBTime += (int) (m_ulGranularity*1000);
  1608. m_ulCallbackID = m_pScheduler->AbsoluteEnter( this,
  1609.                                                       *((HXTimeval*)m_pFakeAudioCBTime));
  1610.     }
  1611. }
  1612. void
  1613. CHXAudioPlayer::SetLive(BOOL bIsLive)
  1614. {
  1615.     m_bIsLive = bIsLive;
  1616.     CHXAudioStream* s = 0;
  1617.     CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
  1618.     for (; lIter != m_pStreamList->End(); ++lIter)
  1619.     {
  1620. s = (CHXAudioStream*) (*lIter);
  1621. s->SetLive(m_bIsLive);
  1622.     }
  1623. }
  1624. void
  1625. CHXAudioPlayer::AudioFormatNowKnown(void)
  1626. {
  1627.     HX_ASSERT(m_bInited);
  1628.     /* When : More than one audio stream created after initialization
  1629.      * and one of them already called this function earlier
  1630.      */
  1631.     if (m_bHasStreams)
  1632.     {
  1633. return;
  1634.     }
  1635.     m_bHasStreams   = TRUE;
  1636.     m_bInited = FALSE;
  1637.     /* internal setup */
  1638.     HX_RESULT theErr = Setup(m_ulGranularity);
  1639.     if (theErr != HXR_OK)
  1640.     {
  1641. IHXErrorMessages* pErrorMessage = NULL;
  1642. m_pContext->QueryInterface(IID_IHXErrorMessages, (void**) &pErrorMessage);
  1643. if (pErrorMessage)
  1644. {
  1645.     pErrorMessage->Report(HXLOG_ERR, theErr, 0, NULL, NULL);
  1646.     pErrorMessage->Release();
  1647. }
  1648. return;
  1649.     }
  1650.     HX_ASSERT(m_bInited);
  1651.     /* If we have not yet resumed, it is simply setting the audio
  1652.      * player from fake timeline mode to audio timeline mode.
  1653.      * Instead if we have already been resumed and are acting as a
  1654.      * fake timeline, we need to kinda pause from being a fake timeline to
  1655.      * an audio timeline, In this process, we may have to seek the audio
  1656.      * device to generate right timesyncs.
  1657.      */
  1658.     /* If we are already resumed, we need to call resume again internally */
  1659.     if (m_bIsResumed)
  1660.     {
  1661. StopFakeTimeline();
  1662. Seek(m_ulCurrentTime);
  1663. // only resume the owner if we are in a play state...
  1664. // otherwise owner will be resumed when the player gets resumed.
  1665. if (m_eState == E_PLAYING)
  1666. {
  1667.     m_Owner->Resume();
  1668. }
  1669.     }
  1670.     else
  1671.     {
  1672. /* Cool! HXPlayer will call resume later */
  1673.     }
  1674. }
  1675. void
  1676. CHXAudioPlayer::RegisterRealAudioStream(CHXAudioStream* pAudioStream)
  1677. {
  1678.     if (!m_pRealAudioStreamList)
  1679.     {
  1680. m_pRealAudioStreamList = new CHXSimpleList;
  1681.     }
  1682.     m_pRealAudioStreamList->AddTail((void*) pAudioStream);
  1683.     m_bAdjustForRealAudio = TRUE;
  1684. }
  1685. void
  1686. CHXAudioPlayer::UnRegisterRealAudioStream(CHXAudioStream* pAudioStream)
  1687. {
  1688.     if (!m_pRealAudioStreamList)
  1689.     {
  1690. return;
  1691.     }
  1692.     LISTPOSITION posStream = m_pRealAudioStreamList->Find((void*) pAudioStream);
  1693.     if (posStream)
  1694.     {
  1695. m_pRealAudioStreamList->RemoveAt(posStream);
  1696.     }
  1697.     /* Check if there are any more RealAudio Streams registered */
  1698.     if (m_pRealAudioStreamList->GetCount() == 0)
  1699.     {
  1700. m_bAdjustForRealAudio = FALSE;
  1701.     }
  1702. }
  1703. BOOL
  1704. CHXAudioPlayer::IsAudioOnlyTrue(void)
  1705. {
  1706.     BOOL bRetValue = TRUE;
  1707.     IHXPlayer* pPlayer = NULL;
  1708.     m_pContext->QueryInterface(IID_IHXPlayer, (void**)&pPlayer);
  1709.     HX_ASSERT(pPlayer);
  1710.     UINT16 uNumSources = pPlayer->GetSourceCount();
  1711.     IUnknown* pUnknown = NULL;
  1712.     IHXStreamSource* pStreamSource = NULL;
  1713.     IHXStream* pStream = NULL;
  1714.     for (UINT16 i=0; bRetValue && i < uNumSources; i++)
  1715.     {
  1716. pPlayer->GetSource(i, pUnknown);
  1717. pUnknown->QueryInterface(IID_IHXStreamSource,
  1718.  (void**) &pStreamSource);
  1719. HX_RELEASE(pUnknown);
  1720. HX_ASSERT(pStreamSource);
  1721. UINT16 uNumStreams = pStreamSource->GetStreamCount();
  1722. for (UINT16 j=0; bRetValue && j < uNumStreams; j++)
  1723. {
  1724.     pStreamSource->GetStream(j, pUnknown);
  1725.     pUnknown->QueryInterface(IID_IHXStream,
  1726.      (void**) &pStream);
  1727.     HX_RELEASE(pUnknown);
  1728.     HX_ASSERT(pStream);
  1729.     IHXValues* pHeader = pStream->GetHeader();
  1730.     if (pHeader)
  1731.     {
  1732. if (!IsThisAudioStream(pHeader))
  1733. {
  1734.     bRetValue = FALSE;
  1735. }
  1736. pHeader->Release();
  1737.     }
  1738.     pStream->Release();
  1739. }
  1740. HX_RELEASE(pStreamSource);
  1741.     }
  1742.     HX_RELEASE(pPlayer);
  1743.     return bRetValue;
  1744. }
  1745. BOOL
  1746. CHXAudioPlayer::IsThisAudioStream(IHXValues* pHeader)
  1747. {
  1748.     CHXSimpleList::Iterator ndxStream = m_pStreamList->Begin();
  1749.     for(; ndxStream != m_pStreamList->End(); ++ndxStream)
  1750.     {
  1751. CHXAudioStream* pAudioStream = (CHXAudioStream*) (*ndxStream);
  1752. IHXValues* pAudioHeader = pAudioStream->GetStreamInfo();
  1753. if (pAudioHeader == pHeader)
  1754. {
  1755.     HX_RELEASE(pAudioHeader);
  1756.     return TRUE;
  1757. }
  1758. HX_RELEASE(pAudioHeader);
  1759.     }
  1760.     return FALSE;
  1761. }
  1762. void
  1763. CHXAudioPlayer::AdjustForRealAudio()
  1764. {
  1765.     UINT32 ulCurrentDeviceTime = m_ulCurrentTime;
  1766. #if defined(HELIX_FEATURE_AUDIO_INACCURATESAMPLING)
  1767.     if (m_bAdjustForRealAudio)
  1768.     {
  1769. CHXAudioStream* pAudioStream = NULL;
  1770.         // Only use a playing audio stream for time adjustment:
  1771.         for (CHXSimpleList::Iterator streamIterator = m_pRealAudioStreamList->Begin();
  1772.              streamIterator != m_pRealAudioStreamList->End();
  1773.              ++streamIterator)
  1774.         {
  1775.             if ( ((CHXAudioStream*) (*streamIterator))->GetState() == E_PLAYING)
  1776.             {
  1777.                 pAudioStream = (CHXAudioStream*) *streamIterator;
  1778.                 break;
  1779.             }
  1780.         }
  1781.         if (pAudioStream)
  1782.         {
  1783.             UINT32 ulAdjustedTime = 0L;
  1784.             double dBytesPlayed = m_Owner->GetNumBytesPlayed();
  1785.             if (HXR_OK == pAudioStream->ConvertCurrentTime( dBytesPlayed,
  1786.                                                             m_ulCurrentTime,
  1787.                                                             ulAdjustedTime))
  1788.             {
  1789.                 UINT32 ulTickCount = HX_GET_TICKCOUNT();
  1790.                 /* This is to avoid stall at end of the presentation */
  1791.                 /* The RA stream may have a duration of say 30 seconds
  1792.                  * but the actual data may be only for 29.9
  1793.                  * seconds. In this case, audio stream will never
  1794.                  * return time more than 29.9 seconds and we will get
  1795.                  * stalled.  To avoid this, we wait for atmost
  1796.                  * MAX_WAIT_AT_SAME_TIME (== max granularity+50 ms) at
  1797.                  * the same timestamp. If we find that we are pushing
  1798.                  * more data in the audio device but the audio stream
  1799.                  * is reporting the same time for the past
  1800.                  * MAX_WAIT_AT_SAME_TIME, we increment our timestamp
  1801.                  * by the real time elapsed since the last update.
  1802.                  * This code will only trigger when we are near the
  1803.                  * end of presentation.
  1804.                  */
  1805.                 if (m_bTimeReturned &&
  1806.                     ulAdjustedTime <= m_ulLastCurrentTimeReturned &&
  1807.                     ulCurrentDeviceTime > m_ulLastDeviceTimeAdjusted &&
  1808.                     ulCurrentDeviceTime - m_ulLastDeviceTimeAdjusted > MAX_WAIT_AT_SAME_TIME)
  1809.                 {
  1810.                     UINT32 ulElapsedTime = CALCULATE_ELAPSED_TICKS(m_ulTimeAdjustDoneAt,
  1811.                                                                    ulTickCount);
  1812.                     if (ulElapsedTime >= MAX_WAIT_AT_SAME_TIME)
  1813.                     {
  1814.                         m_ulCurrentTime = m_ulLastCurrentTimeReturned + ulElapsedTime;
  1815.                         m_ulTimeAdjustDoneAt     = ulTickCount;
  1816.                         m_ulLastDeviceTimeAdjusted  = ulCurrentDeviceTime;
  1817.                     }
  1818.                     else
  1819.                     {
  1820.                         m_ulCurrentTime = ulAdjustedTime;
  1821.                     }
  1822.                 }
  1823.                 else
  1824.                 {
  1825.                     m_ulTimeAdjustDoneAt = ulTickCount;
  1826.                     m_ulCurrentTime      = ulAdjustedTime;
  1827.                 }
  1828.             }
  1829.         }
  1830.     }
  1831. #endif /* HELIX_FEATURE_AUDIO_INACCURATESAMPLING */
  1832.     /* Never go back in time */
  1833.     if (!m_bTimeReturned)
  1834.     {
  1835. m_bTimeReturned     = TRUE;
  1836. m_ulLastCurrentTimeReturned = m_ulCurrentTime;
  1837. m_ulTimeAdjustDoneAt     = HX_GET_TICKCOUNT();
  1838. m_ulLastDeviceTimeAdjusted  = ulCurrentDeviceTime;
  1839.     }
  1840.     else if (m_ulCurrentTime <= m_ulLastCurrentTimeReturned)
  1841.     {
  1842. m_ulCurrentTime = m_ulLastCurrentTimeReturned;
  1843.     }
  1844.     else
  1845.     {
  1846. m_ulLastDeviceTimeAdjusted  = ulCurrentDeviceTime;
  1847. m_ulLastCurrentTimeReturned = m_ulCurrentTime;
  1848.     }
  1849. }
  1850. HX_RESULT
  1851. CHXAudioPlayer::ResumeFakeTimeline(void)
  1852. {
  1853.     HX_RESULT rc = HXR_OK;
  1854.     HX_ASSERT(!m_bHasStreams);
  1855.     HXTimeval lTime = m_pScheduler->GetCurrentSchedulerTime();
  1856.     m_pFakeAudioCBTime->tv_sec = lTime.tv_sec;
  1857.     m_pFakeAudioCBTime->tv_usec = lTime.tv_usec;
  1858.     m_ulIncreasingTimer = m_ulCurrentTime;
  1859.     m_ulLastFakeCallbackTime = HX_GET_TICKCOUNT();
  1860.     *m_pFakeAudioCBTime += (int) (m_ulGranularity*1000);
  1861.     m_ulCallbackID = m_pScheduler->AbsoluteEnter(this,
  1862.                                                  *((HXTimeval*) m_pFakeAudioCBTime));
  1863.     return rc;
  1864. }
  1865. HX_RESULT CHXAudioPlayer::Func()
  1866. {
  1867.     m_ulCallbackID = 0;
  1868.     OnTimerCallback();
  1869.     return HXR_OK;
  1870. }
  1871. HX_RESULT CHXAudioPlayer::StopFakeTimeline(void)
  1872. {
  1873.     HX_RESULT rc = HXR_OK;
  1874.     if(m_ulCallbackID && m_pScheduler)
  1875.     {
  1876.         m_pScheduler->Remove(m_ulCallbackID);
  1877.     }
  1878.     return rc;
  1879. }
  1880. double
  1881. CHXAudioPlayer::NumberOfBytesWritten()
  1882. {
  1883.     return m_Owner->NumberOfBytesWritten();
  1884. }
  1885. double
  1886. CHXAudioPlayer::ConvertMsToBytes(UINT32 ulTime)
  1887. {
  1888.     return m_Owner->ConvertMsToBytes(ulTime);
  1889. }
  1890. void
  1891. CHXAudioPlayer::UpdateStreamLastWriteTime()
  1892. {
  1893.     // Make each stream seek, too, since they own the resampling buffers.
  1894.     CHXAudioStream* s = 0;
  1895.     CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
  1896.     for (; lIter != m_pStreamList->End(); ++lIter)
  1897.     {
  1898. s = (CHXAudioStream*) (*lIter);
  1899. s->UpdateStreamLastWriteTime();
  1900.     }
  1901. }
  1902. void
  1903. CHXAudioPlayer::SaveLastNMilliSeconds(BOOL bSave, UINT32 ulNMilliSeconds)
  1904. {
  1905.     // Make each stream seek, too, since they own the resampling buffers.
  1906.     CHXAudioStream* s = 0;
  1907.     CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
  1908.     for (; lIter != m_pStreamList->End(); ++lIter)
  1909.     {
  1910. s = (CHXAudioStream*) (*lIter);
  1911. s->SaveLastNMilliSeconds(bSave, ulNMilliSeconds);
  1912.     }
  1913. }
  1914. void
  1915. CHXAudioPlayer::RewindPlayer(UINT32 ulTimeToRewind)
  1916. {
  1917.     if (m_pStreamList->GetCount() == 0 || !m_bCanBeRewound)
  1918.     {
  1919. return;
  1920.     }
  1921.     // Make each stream seek, too, since they own the resampling buffers.
  1922.     CHXAudioStream* s = 0;
  1923.     CHXSimpleList::Iterator lIter = m_pStreamList->Begin();
  1924.     for (; lIter != m_pStreamList->End(); ++lIter)
  1925.     {
  1926. s = (CHXAudioStream*) (*lIter);
  1927. s->RewindStream(ulTimeToRewind);
  1928.     }
  1929.     HX_ASSERT(m_llLastWriteTime >= ulTimeToRewind);
  1930.     if (m_llLastWriteTime >= ulTimeToRewind)
  1931.     {
  1932. m_llLastWriteTime -= ulTimeToRewind;
  1933.     }
  1934. }
  1935. HX_RESULT
  1936. CHXAudioPlayer::ProcessAudioHook(PROCESS_ACTION action,
  1937.  IHXAudioHook* pAudioHook)
  1938. {
  1939.     return HXR_OK;
  1940. }
  1941. STDMETHODIMP CHXAudioPlayer::SetError( HX_RESULT theErr )
  1942. {
  1943.     if (theErr != HXR_OK)
  1944.     {
  1945. IHXErrorMessages* pErrorMessage = NULL;
  1946. m_pContext->QueryInterface(IID_IHXErrorMessages, (void**) &pErrorMessage);
  1947. if (pErrorMessage)
  1948. {
  1949.     pErrorMessage->Report(HXLOG_ERR, theErr, 0, NULL, NULL);
  1950.     pErrorMessage->Release();
  1951. }
  1952.     }
  1953.     return HXR_OK;
  1954. }