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

Symbian

开发平台:

C/C++

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