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

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 "hxcom.h"
  36. #include "hxresult.h"
  37. #include "hxengin.h"
  38. #include "ihxpckts.h"   // for IHXBuffer 
  39. #include "hxslist.h"
  40. #include "timeval.h"
  41. #include "hxausvc.h"
  42. #include "auderrs.h"
  43. #include "hxaudev.h"
  44. #include "hxaudses.h"
  45. #include "hxtick.h"
  46. #include "chxpckts.h"
  47. #include "debug.h"
  48. #include "microsleep.h"
  49. //me.
  50. #include "audUnix.h"
  51. #include <errno.h>
  52. #if defined(_THREADED_AUDIO) && defined(_UNIX_THREADS_SUPPORTED)
  53. #include "hxprefs.h"
  54. #endif 
  55. //-1 is usually considered to be no file descriptor.
  56. const int CAudioOutUNIX::NO_FILE_DESCRIPTOR = -1;
  57. const int CAudioOutUNIX::MAX_VOLUME = 100;
  58.     
  59. //XXXgfw We need to clean up the return values. We need to return only PN result codes
  60. //XXXgfw and not RA_AOE codes from interface methods.
  61. CAudioOutUNIX::CAudioOutUNIX() :
  62.     m_pCallback(NULL),
  63.     m_wState( RA_AOS_CLOSED ),
  64.     m_wLastError( RA_AOE_NOERR ),
  65.     m_bMixerPresent(FALSE),
  66.     m_wBlockSize(0),
  67.     m_ulLastNumBytes (0),
  68.     m_ulTotalWritten(0),
  69.     m_bFirstWrite (TRUE),
  70.     m_pPlaybackCountCBTime(0),
  71.     m_PendingCallbackID (0),
  72.     m_bCallbackPending(FALSE),
  73.     m_pWriteList(NULL),
  74.     m_ulDeviceBufferSize(0),
  75. #if defined(_THREADED_AUDIO) && defined(_UNIX_THREADS_SUPPORTED)
  76.     m_mtxWriteListPlayStateLock(NULL),
  77.     m_mtxDeviceStateLock(NULL),
  78.     m_audioThread(NULL),
  79.     m_bUserWantsThreads(TRUE),
  80.     m_ulSleepTime(0),
  81. #endif
  82.     m_pRollbackBuffer(NULL)
  83. {
  84.     //Alloc a Timeval for use with the timer callback.
  85.     m_pPlaybackCountCBTime = new Timeval;
  86.     m_pCallback = new HXPlaybackCountCB(this);
  87.     m_pCallback->AddRef();
  88.     
  89.     //Allco our write buffer list. Want to throw from here? You will, like
  90.     //it or not.
  91.     m_pWriteList = new CHXSimpleList();
  92.     
  93. }
  94. void CAudioOutUNIX::_initAfterContext()
  95. {
  96. #if defined(_THREADED_AUDIO) && defined(_UNIX_THREADS_SUPPORTED)
  97.     HX_ASSERT( m_pContext );
  98.     
  99.     //Find out if the user wants to use threads or not.
  100.     IHXPreferences* pPreferences = NULL;   
  101.     if( m_pContext && HXR_OK == m_pContext->QueryInterface( IID_IHXPreferences, (void **) &pPreferences))
  102.     {
  103.         IHXBuffer *pBuffer = NULL;
  104.         pPreferences->ReadPref("ThreadedAudio", pBuffer);
  105.         if (pBuffer)
  106.         {
  107.             m_bUserWantsThreads = (::atoi((const char*)pBuffer->GetBuffer()) == 1);
  108.             HX_RELEASE(pBuffer);
  109.         }
  110.         HX_RELEASE( pPreferences );
  111.     }    
  112.     if( m_bUserWantsThreads )
  113.     {
  114.         //Initialize the write list and playstate mutex.
  115.         HXMutex::MakeMutex(m_mtxWriteListPlayStateLock );
  116.         HXMutex::MakeMutex( m_mtxDeviceStateLock );
  117.         HXThread::MakeThread( m_audioThread );
  118.     }
  119. #endif    
  120. }
  121. CAudioOutUNIX::~CAudioOutUNIX()
  122. {
  123.     //We must assume that _Imp_Close has already been called. If not, we are 
  124.     //in big trouble.
  125.     if ( m_wState != RA_AOS_CLOSED ) 
  126.     {
  127.         HX_ASSERT( "Device not closed in dtor." == NULL );
  128.     }
  129.     
  130. #if defined(_THREADED_AUDIO) && defined(_UNIX_THREADS_SUPPORTED)
  131.     //Sanity check that the audio thread has exited.
  132.     //XXXgfw ADD a thread join to HXThread and use it instead
  133.     //       of the cond var below in _close and we won't have
  134.     //       to check here at all.
  135. #ifdef _DEBUG    
  136.     UINT32 unThreadID = 0;
  137.     if( m_audioThread )
  138.     {
  139.         m_audioThread->GetThreadId(unThreadID);
  140.         HX_ASSERT( unThreadID == 0 );
  141.     }
  142. #endif    
  143. #endif
  144.     //Clean up the scheduler.
  145.     HX_RELEASE( m_pScheduler );
  146.     
  147.     //Just in case it isn't empty at this point.
  148.     while( m_pWriteList && !m_pWriteList->IsEmpty() )
  149.     {
  150.         IHXBuffer* pBuffer = (IHXBuffer *)(m_pWriteList->RemoveHead());
  151.         HX_RELEASE( pBuffer );
  152.     }
  153.     HX_DELETE(m_pPlaybackCountCBTime);
  154.     HX_RELEASE(m_pCallback);
  155.     HX_DELETE(m_pWriteList);
  156.     HX_VECTOR_DELETE(m_pRollbackBuffer);
  157.     
  158. #if defined(_THREADED_AUDIO) && defined(_UNIX_THREADS_SUPPORTED)
  159.     if( m_bUserWantsThreads )
  160.     {
  161.         HX_DELETE( m_mtxWriteListPlayStateLock );
  162.         HX_DELETE( m_mtxDeviceStateLock );
  163.         HX_DELETE( m_audioThread );
  164.     }
  165. #endif    
  166. }
  167. UINT16 CAudioOutUNIX::_Imp_GetVolume()
  168. {
  169.     if (!m_bMixerPresent)
  170.         _OpenMixer();
  171.     if ( m_bMixerPresent ) 
  172.     {
  173.         m_uCurVolume = _GetVolume();
  174.     }
  175.     return m_uCurVolume;
  176. }
  177. HX_RESULT CAudioOutUNIX::_Imp_SetVolume( const UINT16 uVolume )
  178. {
  179.     HX_RESULT retCode = RA_AOE_NOERR;
  180.     
  181.     //Mixer methods can be called before _Imp_Open
  182.     if( !m_bMixerPresent )
  183.         _OpenMixer();
  184.     //m_uCurVolume is set up at the pnaudev level.
  185.     if( m_bMixerPresent )
  186.     {
  187.         retCode =_SetVolume( uVolume );
  188.     }
  189.     m_wLastError = retCode;
  190.     return m_wLastError;
  191. }
  192. BOOL CAudioOutUNIX::_Imp_SupportsVolume()
  193. {
  194.     return TRUE;
  195. }
  196. HX_RESULT CAudioOutUNIX::_Imp_Open( const HXAudioFormat* pFormat )
  197. {
  198.     HX_RESULT retCode = RA_AOE_NOERR;
  199.     
  200.     //Schedule the timer callback...
  201.     if(m_pContext && !m_pScheduler)
  202.     {
  203.         m_pContext->QueryInterface(IID_IHXScheduler, (void**) &m_pScheduler );
  204.     }
  205.     // Check state. Could already be opened.
  206.     if( !IsOpen() && m_wState != RA_AOS_OPENING )
  207.     {
  208.         retCode = _OpenAudio();
  209.         if( retCode == RA_AOE_NOERR ) 
  210.         {
  211.             m_wBlockSize     = m_ulBytesPerGran;
  212.             m_uSampFrameSize
  213.                 = pFormat->uBitsPerSample / 8;
  214.             
  215.             //Set device state
  216.             LOCK(m_mtxWriteListPlayStateLock);
  217.             m_wState = RA_AOS_OPENING;
  218.             UNLOCK(m_mtxWriteListPlayStateLock);
  219.             //Configure the audio device.
  220.             retCode = _SetDeviceConfig( pFormat );
  221.             if (retCode != RA_AOE_NOERR) 
  222.             {
  223.                 _CloseAudio();
  224.                 m_wState=RA_AOS_CLOSED;
  225.             }
  226.             else
  227.             {
  228. #if defined(_THREADED_AUDIO) && defined(_UNIX_THREADS_SUPPORTED)
  229.                 //We want to sleep as a function of device buffer size.
  230.                 //If we have a small m_ulDeviceBufferSize we can only 
  231.                 //afford to sleep just a little while.
  232.                 HX_ASSERT( m_ulDeviceBufferSize != 0 );
  233.                 m_ulSleepTime = (((float)m_ulDeviceBufferSize/(float)m_uSampFrameSize)/
  234.                                  (float)m_unSampleRate) * 1000 / (float)m_unNumChannels;
  235. #endif
  236.                 if (!m_bMixerPresent)
  237.                     _OpenMixer();   
  238.                 if( _IsSelectable() )
  239.                 {
  240.                     IHXAsyncIOSelection* pAsyncIO = NULL;
  241.                     if( m_pContext && HXR_OK == m_pContext->QueryInterface( IID_IHXAsyncIOSelection, (void**)&pAsyncIO))
  242.                     {
  243.                         pAsyncIO->Add( new HXPlaybackCountCB(this, FALSE), _Imp_GetAudioFd() , PNAIO_WRITE);
  244.                         HX_RELEASE( pAsyncIO );
  245.                     }
  246.                 }
  247.             }
  248.         }
  249.         else
  250.         {
  251.             //Couldn't open the audio device.
  252.             //Just pass through and return the error.
  253.             m_wState=RA_AOS_CLOSED;
  254.         }
  255.         
  256.     }
  257.     // If we're optimizing/minimizing heap, we don't want to do this malloc
  258.     // yet, because we're going to change/minize the value of 
  259.     // m_ulDeviceBufferSize later on.
  260. #ifndef HELIX_CONFIG_MIN_ROLLBACK_BUFFER
  261.     //_SetDeviceConfig should have set the m_ulDeviceBufferSize var.
  262.     if( RA_AOE_NOERR == retCode && !_HardwarePauseSupported() )
  263.     {
  264.         HX_ASSERT( m_ulDeviceBufferSize != 0 );
  265.         if( NULL == m_pRollbackBuffer)
  266.         {
  267.             m_pRollbackBuffer = new UCHAR[m_ulDeviceBufferSize];
  268.             memset( m_pRollbackBuffer, '0', m_ulDeviceBufferSize );
  269.         }
  270.     }
  271. #endif // HELIX_CONFIG_MIN_ROLLBACK_BUFFER
  272. #if defined(_THREADED_AUDIO) && defined(_UNIX_THREADS_SUPPORTED)
  273.     //Start up the audio thread if it isn't going allready from a previous open
  274.     //which sometimes happens.
  275.     
  276.     //Make sure we only have one thread going at a time.
  277.     if( m_bUserWantsThreads && RA_AOE_NOERR==retCode )
  278.     {
  279.         UINT32 unThreadID = 0;
  280.         m_audioThread->GetThreadId(unThreadID);
  281.         
  282.         if( unThreadID == 0 )
  283.             m_audioThread->CreateThread( CAudioOutUNIX::AudioThread, this, 0 );
  284.         
  285.         m_audioThread->GetThreadId(unThreadID);
  286.     }
  287. #endif
  288.     m_wLastError = retCode;
  289.     return m_wLastError;
  290. }
  291. HX_RESULT CAudioOutUNIX::_Imp_Close()
  292. {
  293.     HX_RESULT retCode = RA_AOE_NOERR;
  294.     LOCK(m_mtxWriteListPlayStateLock);
  295.     m_wState = RA_AOS_CLOSING;
  296.     UNLOCK(m_mtxWriteListPlayStateLock);
  297. #if defined(_THREADED_AUDIO) && defined(_UNIX_THREADS_SUPPORTED)
  298.     //The audio thread should exit when the state changes to closed.
  299.     //Wait for it to do so and clean up.
  300.     if( m_bUserWantsThreads )
  301.     {
  302.         m_audioThread->Exit(0);
  303.     }
  304.     
  305. #endif    
  306.     //Reset the device and empty the write buffer.
  307.     //Don't protect this reset with a lock as the audio thread
  308.     //has alread exited.
  309.     retCode = _Imp_Reset();
  310.     
  311.     if( retCode != RA_AOE_DEVNOTOPEN )
  312.     {
  313.         if( _IsSelectable() )
  314.         {
  315.             IHXAsyncIOSelection* pAsyncIO = NULL;
  316.             
  317.             if( HXR_OK == m_pContext->QueryInterface( IID_IHXAsyncIOSelection,
  318.                                                       (void**)&pAsyncIO)
  319.                 )
  320.             {
  321.                 pAsyncIO->Remove(_Imp_GetAudioFd() , PNAIO_WRITE);
  322.                 HX_RELEASE( pAsyncIO );
  323.             }
  324.         }
  325.     }
  326.     //Close the audio device.
  327.     retCode = _CloseAudio();
  328.     
  329.     //Close the mixer device.
  330.     _CloseMixer();
  331.     //Set state.
  332.     LOCK(m_mtxWriteListPlayStateLock);
  333.     m_wState = RA_AOS_CLOSED;
  334.     UNLOCK(m_mtxWriteListPlayStateLock);
  335.     //Remove callback from scheduler
  336.     if (m_bCallbackPending)
  337.     {
  338.         m_pScheduler->Remove(m_PendingCallbackID);
  339.         m_bCallbackPending = FALSE;
  340.     }
  341.     HX_VECTOR_DELETE(m_pRollbackBuffer);
  342.     //return
  343.     m_wLastError = retCode;
  344.     return m_wLastError;
  345. }
  346. //So far no UN*X platforms support this.
  347. HX_RESULT CAudioOutUNIX::_Imp_Seek(ULONG32 ulSeekTime)
  348. {
  349.     m_wLastError = HXR_OK;
  350.     return m_wLastError;
  351. }
  352. HX_RESULT CAudioOutUNIX::_Imp_Pause()
  353. {
  354.     //XXXgfw We really should be closing the device instead of keeping it open to be nice to
  355.     //other procs.
  356.     LOCK(m_mtxWriteListPlayStateLock);
  357.     m_wState = RA_AOS_OPEN_PAUSED;
  358.     UNLOCK(m_mtxWriteListPlayStateLock);
  359.     
  360.     
  361.     if( !_HardwarePauseSupported() )
  362.     {
  363.         // Strategy.
  364.         //--Find out how much we have left in the device's buffer.
  365.         ULONG32 ulBytesPlayed      = _GetBytesActualyPlayed();
  366.         ULONG32 ulNumBytesToRewind = m_ulTotalWritten-ulBytesPlayed;
  367.         
  368.         //Sometimes we can get a value for bytesPlayed that is slightly
  369.         //more then the amount we have actually written. This results
  370.         //in a huge number.
  371.         if( ulBytesPlayed > m_ulTotalWritten )
  372.             ulNumBytesToRewind = 0;
  373.         
  374.         //--and reset player and discard all the data in the device's buffer
  375.         //We will just ignore an error here. That means the buffer will just drain
  376.         //and we will hear it again when they unpause.
  377.         LOCK(m_mtxDeviceStateLock);
  378.         _Reset();
  379.         UNLOCK(m_mtxDeviceStateLock);
  380.         //Make sure we only deal with full samples. Bytes-per-sample*num-channels.
  381.         int nRem = ulNumBytesToRewind%(m_uSampFrameSize*m_unNumChannels);
  382.         ulNumBytesToRewind = ulNumBytesToRewind>nRem ? ulNumBytesToRewind-nRem : 0;
  383.         // In heap-optimized mode, we accept that we're going to lose a 
  384.         // little bit of data after a pause/resume because we are using as
  385.         // small a rollback buffer as we can get away with.
  386. #ifdef HELIX_CONFIG_MIN_ROLLBACK_BUFFER
  387. if( ulNumBytesToRewind > m_ulDeviceBufferSize )
  388. {
  389.    ulNumBytesToRewind = m_ulDeviceBufferSize;
  390. }
  391. #endif // HELIX_CONFIG_MIN_ROLLBACK_BUFFER
  392.         //  and add it to the front of the write buffer.
  393.         IHXBuffer* pNewBuffer = new CHXBuffer();
  394.         pNewBuffer->Set( m_pRollbackBuffer+m_ulDeviceBufferSize-ulNumBytesToRewind,
  395.                          ulNumBytesToRewind );
  396.         LOCK(m_mtxWriteListPlayStateLock);
  397.         m_pWriteList->AddHead(pNewBuffer);        
  398.         pNewBuffer->AddRef();
  399.         UNLOCK(m_mtxWriteListPlayStateLock);
  400.         //--Subtract that from m_ulTotalWritten.
  401.         m_ulTotalWritten -= ulNumBytesToRewind;
  402.         
  403.         _Pause();
  404.     }
  405.     else
  406.     {
  407.         //The hardware device handles the Pause/Resume.
  408.         LOCK(m_mtxDeviceStateLock);
  409.         _Pause();
  410.         UNLOCK(m_mtxDeviceStateLock);
  411.     }
  412.     
  413.     m_wLastError = HXR_OK;
  414.     return m_wLastError;
  415. }
  416. HX_RESULT CAudioOutUNIX::_Imp_Resume()
  417. {
  418.     //XXXgfw We really should be closing and re-opening the device to be nice to other procs.
  419.     LOCK(m_mtxWriteListPlayStateLock);
  420.     m_wState = RA_AOS_OPEN_PLAYING;
  421.     UNLOCK(m_mtxWriteListPlayStateLock);
  422.     //XXXgfw If the two branches of the if are the same maybe get rid of one????
  423.     if( !_HardwarePauseSupported() )
  424.     {
  425.         _Resume();
  426.         _Imp_Write(NULL);
  427.     }
  428.     else
  429.     {
  430.         //The hardware device handles the Pause/Resume.
  431.         _Resume();
  432.         _Imp_Write(NULL);
  433.     }
  434.     
  435.     m_wLastError = HXR_OK;
  436.     return m_wLastError;
  437. }
  438. HX_RESULT CAudioOutUNIX::_Imp_Reset()
  439. {
  440.     HX_RESULT retCode = RA_AOE_NOERR;
  441.     
  442.     if ( m_wState != RA_AOS_CLOSED )
  443.     {
  444.         LOCK(m_mtxDeviceStateLock);
  445.         retCode = _Reset();
  446.         UNLOCK(m_mtxDeviceStateLock);
  447.         LOCK(m_mtxWriteListPlayStateLock);
  448.         while (m_pWriteList && m_pWriteList->GetCount() > 0)
  449.         {
  450.             IHXBuffer* pBuffer = (IHXBuffer *)(m_pWriteList->RemoveHead());
  451.             HX_RELEASE( pBuffer );
  452.         }
  453.         UNLOCK(m_mtxWriteListPlayStateLock);
  454.         m_ulTotalWritten  = 0;
  455.         m_bFirstWrite     = TRUE;
  456.         m_ulLastNumBytes  = 0;
  457.     }
  458.     
  459.     m_wLastError = retCode;
  460.     return m_wLastError;
  461. }
  462. HX_RESULT CAudioOutUNIX::_Imp_Drain()
  463. {
  464.     HX_RESULT retCode = RA_AOE_NOERR;
  465.     LOCK(m_mtxWriteListPlayStateLock);
  466.     if( m_wState != RA_AOS_CLOSED )
  467.     {
  468.         retCode = _Drain();
  469.     }
  470.     while( m_pWriteList && !m_pWriteList->IsEmpty() )
  471.     {
  472.         IHXBuffer* pBuffer = (IHXBuffer *)(m_pWriteList->RemoveHead());
  473.         HX_RELEASE( pBuffer );
  474.     }
  475.     UNLOCK(m_mtxWriteListPlayStateLock);
  476.     
  477.     m_wLastError = retCode;
  478.     return m_wLastError;
  479. }
  480. HX_RESULT CAudioOutUNIX::_Imp_CheckFormat( const HXAudioFormat* pFormat )
  481. {
  482.     HX_RESULT retCode = RA_AOE_NOERR;
  483.     m_wLastError       = HXR_OK;
  484.     retCode = _CheckFormat( pFormat );
  485.     if( RA_AOE_NOERR != retCode && RA_AOE_DEVBUSY != retCode )
  486.     {
  487.         m_wLastError = HXR_FAILED;
  488.     }
  489.     else
  490.     {
  491.         m_wLastError = HXR_OK;
  492.     }
  493.     
  494.     return m_wLastError;
  495. }
  496. HX_RESULT CAudioOutUNIX::_Imp_GetCurrentTime( ULONG32& ulCurrentTime )
  497. {
  498.     ULONG32 ulTime   = 0;
  499.     UINT64  ulBytes  = 0;
  500.     LOCK(m_mtxWriteListPlayStateLock);
  501.     ulBytes = _GetBytesActualyPlayed();
  502.     UNLOCK(m_mtxWriteListPlayStateLock);
  503.     
  504.     ulTime = (ULONG32)(( ( (double)ulBytes/(double)m_uSampFrameSize)/(double)m_unSampleRate) * 1000.0 / (double)m_unNumChannels);
  505.     
  506.     //Not used anywhere but belongs to CHXAudioDevice so we must set it.
  507.     m_ulCurrentTime  = ulTime;
  508.     //Set the answer.
  509.     ulCurrentTime = ulTime;
  510.     m_wLastError = HXR_OK;
  511.     return HXR_OK;
  512. }
  513. void CAudioOutUNIX::DoTimeSyncs()
  514. {
  515.     ReschedPlaybackCheck();
  516.     OnTimeSync();
  517.     return;
  518. }
  519. HX_RESULT CAudioOutUNIX::ReschedPlaybackCheck()
  520. {
  521.     HX_RESULT retCode = HXR_OK;
  522.     
  523.     if(!m_bCallbackPending)
  524.     {
  525.         HX_ASSERT( m_pCallback );
  526.         if(m_pCallback)
  527.         {
  528.             *m_pPlaybackCountCBTime += (int)(500*m_ulGranularity);
  529.             m_bCallbackPending = TRUE;
  530.             m_PendingCallbackID = m_pScheduler->AbsoluteEnter( m_pCallback,*((HXTimeval*)m_pPlaybackCountCBTime));
  531.         }
  532.         else
  533.         {
  534.             retCode = HXR_OUTOFMEMORY;
  535.         }
  536.     }
  537.     m_wLastError = retCode;
  538.     return m_wLastError;
  539. }
  540. UINT16 CAudioOutUNIX::_NumberOfBlocksRemainingToPlay(void)
  541. {
  542.     UINT32 bytesBuffered = 0;
  543.     //We have to go through all of the buffers and count the size. Even though
  544.     //we always write m_wBlockSize buffers, we can get off-sized buffs because
  545.     //of the pause/resume code. When we pause we rewind however many bytes have
  546.     //not been played yet in the buffer.
  547.     
  548.     LOCK(m_mtxWriteListPlayStateLock);
  549.     if( m_pWriteList )
  550.     {
  551.         LISTPOSITION i = m_pWriteList->GetHeadPosition();
  552.         
  553.         while( i )
  554.         {
  555.             bytesBuffered += ((IHXBuffer *)m_pWriteList->GetAt(i)) -> GetSize();
  556.             m_pWriteList->GetNext(i);
  557.         }
  558.     }
  559.     bytesBuffered += (m_ulTotalWritten - _GetBytesActualyPlayed());    
  560.     UNLOCK(m_mtxWriteListPlayStateLock);
  561.     return bytesBuffered / m_wBlockSize + 1;
  562. }
  563. CAudioOutUNIX::HXPlaybackCountCB::~HXPlaybackCountCB()
  564. {
  565. }
  566. STDMETHODIMP CAudioOutUNIX::HXPlaybackCountCB::QueryInterface( REFIID riid, void** ppvObj )
  567. {
  568.     HX_RESULT retCode = HXR_OK;
  569.     
  570.     if( IsEqualIID(riid, IID_IHXCallback) )
  571.     {
  572.         AddRef();
  573.         *ppvObj = (IHXCallback*)this;
  574.     }
  575.     else if (IsEqualIID(riid, IID_IUnknown))
  576.     {
  577.         AddRef();
  578.         *ppvObj = this;
  579.     }
  580.     else
  581.     {
  582.         *ppvObj = NULL;
  583.         retCode = HXR_NOINTERFACE;
  584.     }
  585.     return retCode;
  586. }
  587. STDMETHODIMP_(ULONG32) CAudioOutUNIX::HXPlaybackCountCB::AddRef()
  588. {
  589.     return InterlockedIncrement(&m_lRefCount);
  590. }
  591. STDMETHODIMP_(ULONG32) CAudioOutUNIX::HXPlaybackCountCB::Release()
  592. {
  593.     
  594.     if (InterlockedDecrement(&m_lRefCount) > 0)
  595.     {
  596.         return m_lRefCount;
  597.     }
  598.     
  599.     delete this;
  600.     return HXR_OK;
  601. }
  602. STDMETHODIMP CAudioOutUNIX::HXPlaybackCountCB::Func(void)
  603. {
  604.     if (m_pAudioDeviceObject)
  605.     {
  606.         if(!m_timed)
  607.         {
  608.             //m_pAudioDeviceObject->_Imp_Write(NULL);
  609.         }
  610.         else
  611.         {
  612.             m_pAudioDeviceObject->m_bCallbackPending  = FALSE;
  613.             m_pAudioDeviceObject->_Imp_Write(NULL);
  614.             m_pAudioDeviceObject->DoTimeSyncs();
  615.         }
  616.     }
  617.     return HXR_OK;
  618. }
  619. HX_RESULT CAudioOutUNIX::_Pause()
  620. {
  621.     return RA_AOE_NOTSUPPORTED;
  622. }
  623. HX_RESULT CAudioOutUNIX::_Resume()
  624. {
  625.     return RA_AOE_NOTSUPPORTED;
  626. }
  627. BOOL CAudioOutUNIX::_IsSelectable() const
  628. {
  629.     return TRUE;
  630. }
  631. BOOL CAudioOutUNIX::_HardwarePauseSupported() const
  632. {
  633.     return FALSE;
  634. }
  635. #if defined(_THREADED_AUDIO) && defined(_UNIX_THREADS_SUPPORTED)
  636. void* CAudioOutUNIX::AudioThread(void *thisPointer )
  637. {
  638.     BOOL bReadyToExit=FALSE;
  639.     CAudioOutUNIX* that = (CAudioOutUNIX*)thisPointer;
  640.     while(!bReadyToExit)
  641.     {
  642.         //If you want to destroy frame rate just sit on this lock for 
  643.         //a while. If you don't, make sure this is a *very* fast loop.
  644.         that->m_mtxWriteListPlayStateLock->Lock();
  645.         that->m_mtxDeviceStateLock->Lock();
  646.         if(that->m_wState!=RA_AOS_CLOSED && that->m_wState!=RA_AOS_CLOSING)
  647.         {
  648.             if( that->m_pWriteList->GetCount() > 0 && that->m_wState == RA_AOS_OPEN_PLAYING )
  649.             {
  650.                 that->_PushBits();
  651.             }
  652.         }
  653.         else
  654.         {
  655.             bReadyToExit=TRUE;
  656.         }
  657.         that->m_mtxDeviceStateLock->Unlock();
  658.         that->m_mtxWriteListPlayStateLock->Unlock();
  659.         //OK, sleep the amount of time it takes to play 1/4 of the device's buffer.
  660.         microsleep(that->m_ulSleepTime/4); 
  661.     }
  662.     //Signal the parent thread that we are done.
  663.     that->m_audioThread->Exit(0);
  664.     return (void*)0;
  665. }
  666. #endif
  667. // Don't protect anything in this method with mutexes. It has 
  668. // already been done from where it is called from. But mostly
  669. // because we aren't using recursive mutexes yet.
  670. ULONG32 CAudioOutUNIX::_PushBits()
  671. {
  672.     IHXBuffer* pBuffer  = NULL;
  673.     UCHAR*      pData    = 0;
  674.     ULONG32     ulBufLen = 0;
  675.     LONG32      lCount   = 0;
  676.     //We are going to try and write. Grab the head and do it.
  677.     pBuffer  = (IHXBuffer*)m_pWriteList->RemoveHead();
  678.     pData    = pBuffer->GetBuffer();
  679.     ulBufLen = pBuffer->GetSize();
  680.     //
  681.     // Now, in case there is no mixer present, do the volume manually.
  682.     //
  683.     UCHAR* pNoVolumeData = NULL;
  684.     
  685.     if ( !m_bMixerPresent )
  686.     {
  687.         //Save a copy of the non-volume mutated sound data. If we don't, our
  688.         //rewind buffer will fill up with volume modified data and it will get
  689.         //run through the manual sound code again.
  690.         if( !_HardwarePauseSupported() )
  691.         {
  692.             pNoVolumeData = new UCHAR[ ulBufLen ];
  693.             memcpy( pNoVolumeData, pData, ulBufLen ); /* Flawfinder: ignore */
  694.         }
  695.         
  696.         int i, j = ulBufLen / sizeof(short);
  697.         short *s = (short *)pData;
  698.         for (i = 0 ; i < j ; i++) 
  699.         {
  700.             s[i] = ((int)s[i]) * (m_uCurVolume) / MAX_VOLUME;
  701.         }
  702.     }
  703.     UNLOCK(m_mtxWriteListPlayStateLock);
  704.     _WriteBytes(pData, ulBufLen, lCount);
  705.     LOCK(m_mtxWriteListPlayStateLock);
  706.     //Make sure we wrote the whole buffer like we wanted to.
  707.     if( lCount!=-1 && lCount != ulBufLen)
  708.     {
  709.         //In release we can ignore this...I guess. But why didn't we?
  710.         HX_ASSERT( "We ALWAYS write full buffers!" == NULL );
  711.     }
  712.     
  713.     // Check for bad write...then check errno; write could be interrupted
  714.     // by setitimer and sigalarm. If so, loop and try writing again.
  715.     if ( lCount == -1 )
  716.     {
  717.         //EAGAIN should *never* happen as we are checking to make sure we don't
  718.         //block on this call....
  719.         if ( errno == EAGAIN) 
  720.         {
  721.             HX_ASSERT( "We shouldn't be blocking here..."==NULL);
  722.         }
  723.     }
  724.     if( lCount > 0 )
  725.     {
  726.         m_ulTotalWritten += lCount;
  727.         //We only need to do this if the hardware doesn't support pause.
  728.         if( !_HardwarePauseSupported() )
  729.         {
  730.             // If we wrote to the device we need to keep a copy of the 
  731.             // data our device buffer. We use this to 'rewind' the data
  732.             // in case we get paused.
  733.             //If we could write lCount without blocking then there was at 
  734.             //least that much room in the device and since m_pRollbackBuffer
  735.             //is as large as the devices buffer, we can safely shift and copy.
  736.             //Add the new stuff to the end pushing the rest of the data forward.
  737.             //We need this sanity check to prevent a very hard bug to find.
  738.             //Don't get rid of it! If this asserts it *really* means something.
  739.             //The first thing to check is the __powerpc defines that determine
  740.             //buffer size.
  741.             HX_ASSERT( lCount <= m_ulDeviceBufferSize );
  742.             // Ok, now that we've asserted to catch the
  743.             // bug, let's prevent the crash.
  744.             if (lCount > m_ulDeviceBufferSize) lCount = m_ulDeviceBufferSize;
  745.             memmove( m_pRollbackBuffer, m_pRollbackBuffer+lCount, m_ulDeviceBufferSize-lCount); /* Flawfinder: ignore */
  746.             
  747.             //Now copy in the new data at the end of the buffer.
  748.             if( NULL != pNoVolumeData )
  749.                 memcpy( m_pRollbackBuffer+m_ulDeviceBufferSize-lCount, pNoVolumeData, lCount ); /* Flawfinder: ignore */
  750.             else
  751.                 memcpy( m_pRollbackBuffer+m_ulDeviceBufferSize-lCount, pData, lCount );  /* Flawfinder: ignore */
  752.         }
  753.     }
  754.     HX_VECTOR_DELETE(pNoVolumeData);
  755.     HX_RELEASE( pBuffer );
  756.         
  757.     return lCount;
  758. }
  759. HX_RESULT CAudioOutUNIX::_Imp_Write( const HXAudioData* pAudioOutHdr )
  760. {
  761.     IHXBuffer* pBuffer  = NULL;
  762.     ULONG32     ulCount  = 0;
  763.     
  764.     //_Imp_Write won't be called if the device couldn't be opened. But,
  765.     //Just in case.
  766.     if( !IsOpen() )
  767.     {
  768.         return RA_AOE_DEVNOTOPEN;
  769.     }
  770.     //Schedule callbacks.
  771.     if( m_bFirstWrite && pAudioOutHdr )
  772.     {
  773.         m_bFirstWrite = FALSE;
  774.         //  Initialize the playback callback time.
  775.         HXTimeval lTime = m_pScheduler->GetCurrentSchedulerTime();
  776.         m_pPlaybackCountCBTime->tv_sec = lTime.tv_sec;
  777.         m_pPlaybackCountCBTime->tv_usec = lTime.tv_usec;
  778.         //  Scheduler playback callback.
  779.         ReschedPlaybackCheck();
  780.     }
  781.     //Blindly add the incoming data to the tail of the writelist.
  782.     //Some audio devices have very small device buffers. If this
  783.     //buffer is smaller than our incoming block size we must break
  784.     //up the incoming buffer into smaller chuncks. We want to be
  785.     //able to fix *at least* two(1.5?) chunks into the buffer so we don't
  786.     //block all the time.
  787.     if( pAudioOutHdr != NULL )
  788.     {
  789.         LOCK(m_mtxWriteListPlayStateLock);
  790.         int nBuffSize = pAudioOutHdr->pData->GetSize();
  791.         // In heap-optimized mode, this is where we set the value of 
  792.         // m_ulDeviceBufferSize, and malloc the buffer.
  793. #ifdef HELIX_CONFIG_MIN_ROLLBACK_BUFFER
  794. if( m_ulDeviceBufferSize != nBuffSize )
  795. {
  796.             m_ulDeviceBufferSize = nBuffSize;
  797.          HX_VECTOR_DELETE(m_pRollbackBuffer);
  798.             m_pRollbackBuffer = new UCHAR[m_ulDeviceBufferSize];
  799. }
  800. #endif // HELIX_CONFIG_MIN_ROLLBACK_BUFFER
  801. // Shouldn't this be ">=" and not ">"?
  802.         if( m_ulDeviceBufferSize >= nBuffSize )
  803.         {
  804.             //No need to break it up.
  805.             IHXBuffer* pTmpBuff = pAudioOutHdr->pData;
  806.             m_pWriteList->AddTail(pTmpBuff);
  807.             pTmpBuff->AddRef();
  808.         }
  809.         else
  810.         {
  811.             //Break it up. First find the correct size.
  812.             while( nBuffSize >= m_ulDeviceBufferSize )
  813.                 nBuffSize = nBuffSize>>1;
  814.             //Now make sure we never break a buffer apart in the
  815.             //middle of a Frame.
  816.             int nRem = nBuffSize%(m_uSampFrameSize*m_unNumChannels);
  817.             nBuffSize = nBuffSize>nRem ? nBuffSize-nRem : nBuffSize ;
  818.             //March through the buffer, breaking it up.
  819.             UCHAR* pData  = pAudioOutHdr->pData->GetBuffer();
  820.             int    nLimit = pAudioOutHdr->pData->GetSize();
  821.             int    nPtr   = 0;
  822.             while( nPtr < nLimit )
  823.             {
  824.                 IHXBuffer* pNewBuffer = new CHXBuffer();
  825.                 if( nPtr+nBuffSize <= nLimit )
  826.                 {
  827.                     pNewBuffer->Set( pData+nPtr, nBuffSize );
  828.                 }
  829.                 else
  830.                 {
  831.                     pNewBuffer->Set( pData+nPtr, (nLimit-nPtr) );
  832.                 }
  833.                 m_pWriteList->AddTail( pNewBuffer );
  834.                 pNewBuffer->AddRef();
  835.                 nPtr += nBuffSize;
  836.             }
  837.         }
  838.         UNLOCK(m_mtxWriteListPlayStateLock);
  839.     }
  840. #if defined(_THREADED_AUDIO) && defined(_UNIX_THREADS_SUPPORTED)
  841.     //If we are using threaded audio just return and let the audio thread
  842.     //grab the data and write it to the device.
  843.     if( m_bUserWantsThreads )
  844.     {
  845.         return RA_AOE_NOERR;
  846.     }
  847. #endif    
  848.     // ----------- Threaded audio note:
  849.     //
  850.     // We don't need to protect anything below here with the mutex
  851.     // because we will never get here if we are running with threaded
  852.     // audio.
  853.     //
  854.     //XXXgfw This doesn't seem to be doing any good anymore. So I am taking it
  855.     //       out
  856. //      //Check for underflow here and do what? Pause it?
  857. //      if( m_wState!=RA_AOS_OPEN_PAUSED && m_pWriteList->GetCount() <= 0 )
  858. //      {  
  859. //          ULONG32 ulTmp  = 0;
  860.         
  861. //          _GetRoomOnDevice(ulTmp);
  862. //          //there better be stuff in the device because we wouldn't get this
  863. //          //far if we were not in an OPEN/Playing state.
  864. //          if( ulTmp >= m_ulDeviceBufferSize )
  865. //          {
  866. //              //XXXgfw, read below...
  867. //              //We aren't writting anything and there is no 
  868. //              //data in the device buffer. Lions, tigers and bears..
  869. //              //Maybe we should just do a _Pause here to keep
  870. //              //the byte count from the audio device correct?
  871. //              HX_ASSERT( "UNDERFLOW in AUDIO DEVICE" == NULL );
  872. //          }
  873. //      }
  874.     //Just return if there is nothing to do 
  875.     if( m_pWriteList->GetCount() <= 0 || m_wState==RA_AOS_OPEN_PAUSED )
  876.     {
  877.         return RA_AOE_NOERR;
  878.     }
  879.     // 
  880.     // We only want to write to the device if we can send the
  881.     // whole buffer. If there isn't that much room then just
  882.     // put the buffer back in the front of the que.
  883.     //
  884.     ULONG32   ulBytesAvailable = 0;
  885.     HX_RESULT code             = RA_AOE_NOERR;
  886.     
  887.     code = _GetRoomOnDevice(ulBytesAvailable);
  888.     if( RA_AOE_NOERR != code )
  889.     {
  890.         //What? Can't get the room on the device for some reason.
  891.         m_wLastError = code;
  892.         return m_wLastError;
  893.     }
  894.     //Peek at the head.
  895.     pBuffer = (IHXBuffer*)m_pWriteList->GetHead();
  896.     if( NULL==pBuffer || ulBytesAvailable < pBuffer->GetSize())
  897.     {
  898.         m_wLastError = RA_AOE_NOERR;
  899.         return m_wLastError;
  900.     }
  901.     
  902.     ulCount = _PushBits();
  903.     
  904.     if ( m_bFirstWrite )
  905.     {
  906.         m_bFirstWrite = FALSE;
  907.             
  908.         /*  Initialize the playback callback time. */
  909.         HXTimeval lTime = m_pScheduler->GetCurrentSchedulerTime();
  910.         m_pPlaybackCountCBTime->tv_sec = lTime.tv_sec;
  911.         m_pPlaybackCountCBTime->tv_usec = lTime.tv_usec;
  912.             
  913.         /*  Scheduler playback callback. */
  914.         ReschedPlaybackCheck();
  915.     }
  916.     //make sure the device is kept full.
  917.     if( m_pWriteList->GetCount()>0 && ulCount>0 )
  918.         _Imp_Write(NULL);
  919.     return m_wLastError;
  920.     
  921. }
  922. HX_RESULT CAudioOutUNIX::_CheckFormat( const HXAudioFormat* pFormat )
  923. {
  924.     return RA_AOE_NOERR;
  925. }