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

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. /*******************************************************************
  36.  * audaix.cpp
  37.  *
  38.  * CLASS: CAudioOutAIX
  39.  *
  40.  * DESCRIPTION: AIX & UMS specific audio class implementation
  41.  *   
  42.  *******************************************************************/
  43. #include <signal.h> // for  getenv()
  44. #include <stdio.h>
  45. #include <stdlib.h> // for  getenv()
  46. #include <math.h>
  47. #include <sys/types.h>
  48. #include <stropts.h> // for I_FLUSH ioctl
  49. #include <sys/conf.h>
  50. #include <sys/stat.h>
  51. #include <sys/file.h>
  52. #include <sys/ioctl.h>
  53. #include <sys/errno.h>
  54. #include <fcntl.h>
  55. #include <unistd.h>
  56. #include <errno.h>
  57. #include <string.h>
  58. #include <sys/audio.h>
  59. #include "hxcom.h"
  60. #include "hxresult.h"
  61. #include "hxengin.h"
  62. #include "ihxpckts.h"   // for IHXBuffer 
  63. #include "hxslist.h"
  64. #include "timeval.h"
  65. #include "audaix.h"
  66. #include "hxaudses.h"
  67. #include "hxtick.h"
  68. #include "chxpckts.h"
  69. #include "debug.h"
  70. #include "hxstrutl.h"
  71. #include <UMSBAUDDevice.xh>
  72. #include <UMSAudioDevice.xh>
  73. #include <UMSAudioTypes.xh>
  74. struct IHXCallback;  // forward declaration needed for callback.
  75. // One and ONLY one of the following must be uncommented.  They determine the
  76. // strategy used to determine the elaped time. The first is the preferred.
  77. #define AIX_TIME_BYTES_WRITTEN         // function of bytes written vrs bytes in buffer.
  78. //**********************************************************************
  79. //  constants.
  80. //
  81. // in UMS, balance is really an initial pan setting. 
  82. // -100 = hard left, 0 is centered, 100 = hard right
  83. static const LONG32   lDefaultBalance = 0;   
  84. static const LONG32   lDefaultVolume = 50;   // UMS range is 0..100
  85. static LONG32   zlCurrentVolume = 50;   // UMS range is 0..100
  86. // this is sufficient for PCI devices, for microchannel devices, set the
  87. // envar AUDIODEV to "/dev/maud0".
  88. static const char *   szDefaultPortFilename = "/dev/paud0";
  89. static const char *   moduleName = "CAudioOutAIX"; 
  90. // this will point to the SOM environment needed by the UMS subsystem
  91. static Environment*   gpSomEnvironment                  = NULL;
  92. // static local routines, used to translate UMS message code to RMA types.
  93. static const char *   getUMSAudioDeviceError( UMSAudioDevice_ReturnCode );
  94. static audio_error    UMSErrorCodeToRACode( UMSAudioDevice_ReturnCode ); 
  95. CAudioOutAIX::CAudioOutAIX() 
  96.   : mixm_wID( -1 ),
  97.     m_wState( RA_AOS_CLOSED ),
  98.     m_wLastError( RA_AOE_NOERR ),
  99.     m_bMixerPresent(FALSE),
  100.     m_wBlockSize(0),
  101.     m_ulLastNumBytes (0),
  102.     m_ulBytesRemaining(0),
  103.     m_ulTotalWritten(0),
  104.     m_bFirstWrite (TRUE),
  105.     m_pPlaybackCountCBTime(NULL),
  106.     m_PendingCallbackID (0),
  107.     m_bCallbackPending(FALSE),
  108.     m_paused(FALSE),
  109.     m_pWriteList(NULL),
  110.     m_lLeftGain(100),
  111.     m_lRightGain(100)
  112. {
  113.   
  114.     // set up UMS environment.
  115.     gpSomEnvironment = somGetGlobalEnvironment();
  116.     HX_ASSERT( gpSomEnvironment );
  117.     m_pAudioDevice   = new UMSBAUDDevice();
  118.     
  119.     // Get AUDIODEV environment var to find audio device of choice
  120.     char *adev = (char*)getenv("AUDIODEV"); /* Flawfinder: ignore */
  121.     char *mdev = (char*)getenv("MIXERDEV"); /* Flawfinder: ignore */
  122.     // Use defaults if no environment variable is set.
  123.     m_DevName[26]    = NULL;
  124.     m_DevCtlName[26] = NULL;
  125.     if (adev && strlen(adev) > 0)
  126.         SafeStrCpy(m_DevName, adev, 26);
  127.     else
  128.         SafeStrCpy(m_DevName, szDefaultPortFilename, 26);
  129.     m_pPlaybackCountCBTime = new Timeval;
  130.     if (mdev && strlen(mdev) > 0)
  131.         SafeStrCpy(m_DevCtlName, mdev, 26);
  132.     else
  133.         SafeStrCpy(m_DevCtlName, szDefaultPortFilename, 26);
  134.     
  135.     m_pWriteList = new CHXSimpleList();
  136.     // now configure our device.
  137.     m_pAudioDevice->set_audio_format_type  ( gpSomEnvironment, "PCM" );
  138.     m_pAudioDevice->set_number_format      ( gpSomEnvironment, 
  139.      "TWOS_COMPLEMENT" );
  140.     m_pAudioDevice->set_byte_order         ( gpSomEnvironment, "MSB" ); 
  141.     m_pAudioDevice->set_time_format        ( gpSomEnvironment, 
  142.      UMSAudioTypes_Msecs ); 
  143.     
  144.     m_pAudioDevice->set_balance   ( gpSomEnvironment, lDefaultBalance );  
  145.     m_pAudioDevice->set_volume    ( gpSomEnvironment, zlCurrentVolume );
  146.     m_pAudioDevice->enable_output ( gpSomEnvironment, "LINE_OUT", 
  147.     &m_lLeftGain, &m_lRightGain  );
  148. }
  149. CAudioOutAIX::~CAudioOutAIX()
  150. {
  151.     if ( m_wState != RA_AOS_CLOSED ) 
  152.       {
  153. _Imp_Close();
  154. m_wState = RA_AOS_CLOSED;
  155.       }
  156.     
  157.     mixm_wID = -1;
  158.     m_wState =  RA_AOS_CLOSED;
  159.     m_wLastError = RA_AOE_NOERR;
  160.     m_bMixerPresent = FALSE;
  161.     m_wBlockSize       = 0;
  162.     m_ulLastNumBytes   = 0;
  163.     m_ulBytesRemaining = 0;
  164.     m_ulTotalWritten   = 0;
  165.     
  166.     if (m_pScheduler) 
  167.       m_pScheduler->Release();
  168.     
  169.     m_PendingCallbackID = 0;
  170.     m_bCallbackPending = TRUE; 
  171.     
  172.     while(!m_pWriteList->IsEmpty())
  173.       {
  174. IHXBuffer* pBuffer = (IHXBuffer*)m_pWriteList->RemoveHead();
  175. HX_RELEASE(pBuffer);
  176.       }
  177.     HX_DELETE(m_pWriteList);
  178.     
  179.     delete m_pAudioDevice;
  180. }
  181. /*
  182.  *    I do not open /dev/mixer to control volume, the volume is scaled
  183.  *    by the UMS device.
  184.  *    The device's volume has a range of 0..100.
  185.  */
  186. UINT16 CAudioOutAIX::_Imp_GetVolume()
  187. {
  188.     long volume;
  189.     m_pAudioDevice->get_volume( gpSomEnvironment, &volume );
  190.     return zlCurrentVolume;
  191.     return (UINT16)volume;
  192. }
  193. HX_RESULT CAudioOutAIX::_Imp_SetVolume( const UINT16 uVolume )
  194. {
  195.     m_pAudioDevice->set_volume( gpSomEnvironment, (long)uVolume );
  196.     zlCurrentVolume = uVolume;
  197.     return RA_AOE_NOERR;
  198. }
  199. /*
  200.  *  All UMS audio devices support volume.
  201.  */
  202. BOOL CAudioOutAIX::_Imp_SupportsVolume()
  203. {
  204.     return TRUE;
  205. }
  206. HX_RESULT CAudioOutAIX:: _Imp_Open( const HXAudioFormat* pFormatSupplied )
  207. {
  208.     if( pFormatSupplied != NULL )
  209.       m_pAudioFormat = pFormatSupplied;
  210.     
  211.     HX_ASSERT( m_pAudioFormat != NULL ); 
  212.     
  213.     // Get the core scheduler interface; Use this to schedule polling
  214.     // the audio device for number of bytes played.
  215.     if ( m_pContext )
  216.     {
  217. m_pContext->QueryInterface(IID_IHXScheduler, (void**) &m_pScheduler );
  218.     }
  219.     // Check state. Could already be opened.
  220.     if ( m_wState == RA_AOS_OPEN_PAUSED  || 
  221.          m_wState == RA_AOS_OPEN_PLAYING ||
  222.          m_wState == RA_AOS_OPENING )
  223.       return RA_AOE_NOERR;
  224.     
  225.     m_ulBlocksProcessed = 0;
  226.     UMSAudioDevice_ReturnCode retCode =  
  227.       m_pAudioDevice->open( gpSomEnvironment, m_DevName, "PLAY", 0 ); 
  228.     
  229.     if( retCode != UMSAudioDevice_Success ) 
  230.       {
  231.         return HXR_FAILED;
  232.       }
  233.     
  234.     m_wBlockSize = m_ulBytesPerGran;  //m_pAudioFormat->uMaxBlockSize;
  235.     m_uSampFrameSize = m_pAudioFormat->uBitsPerSample / 8;
  236.     
  237.     // Set device state
  238.     m_wState = RA_AOS_OPENING;
  239.     
  240.     // Configure the audio device.
  241.     AUDIOERROR iVal = SetDeviceConfig( m_pAudioFormat );
  242.     
  243.     return RA_AOE_NOERR;
  244. }
  245. HX_RESULT CAudioOutAIX::_Imp_Close()
  246. {
  247.     m_wState = RA_AOS_CLOSED;
  248.     
  249.     // this will force the player to play all remaining data.  If passed TRUE, 
  250.     // the call will block until finished playing. Do we want to call this?
  251.     // m_pAudioDevice->play_remaining_data( gpSomEnvironment, TRUE );
  252.     _Imp_Write(NULL);
  253.     
  254.     m_pAudioDevice->stop( gpSomEnvironment );
  255.     m_pAudioDevice->close( gpSomEnvironment );
  256.     
  257.     // Remove callback from scheduler
  258.     if (m_bCallbackPending)
  259.       {
  260. m_pScheduler->Remove(m_PendingCallbackID);
  261. m_bCallbackPending = FALSE;
  262.     }
  263.     
  264.     return RA_AOE_NOERR;
  265. }
  266. HX_RESULT CAudioOutAIX::_Imp_Write( const HXAudioData* pAudioOutHdr )
  267. {
  268.     IHXBuffer* pBuffer = NULL;
  269.     UCHAR* pData = NULL;
  270.     UMSAudioTypes_Buffer adBuffer;
  271.     UINT32* pTimeStamp = NULL;
  272.     
  273.     if ( m_bFirstWrite && pAudioOutHdr)
  274.     {
  275.         /*  Initialize the playback callback time. */
  276.         HXTimeval lTime = m_pScheduler->GetCurrentSchedulerTime();
  277.         m_pPlaybackCountCBTime->tv_sec = lTime.tv_sec;
  278.         m_pPlaybackCountCBTime->tv_usec = lTime.tv_usec;
  279. m_bFirstWrite = FALSE;
  280. ReschedPlaybackCheck();
  281.     }
  282.     if (pAudioOutHdr)
  283.       {
  284.         ++m_ulBlocksProcessed ; 
  285. DPRINTF(D_INFO, ("_Imp_Write: buf len in milli-seconds: %lun",
  286.  ((ULONG32) (( 1000.0
  287.        / (m_num_channels * m_uSampFrameSize * m_sample_rate))
  288.      *  pAudioOutHdr->pData->GetSize()))));
  289.       }
  290.     
  291.     // If we are paused, just add the block to the end of the WriteList.
  292.     if (m_paused)
  293.     {
  294. if (!pAudioOutHdr)
  295.     return m_wLastError = RA_AOE_NOERR;
  296. IHXBuffer* pNewBuffer = new CHXBuffer();
  297. pNewBuffer->Set(pAudioOutHdr->pData->GetBuffer(),
  298. pAudioOutHdr->pData->GetSize());
  299. pNewBuffer->AddRef();
  300. m_pWriteList->AddTail(pNewBuffer);
  301. return  RA_AOE_NOERR;
  302.     }
  303.  
  304.     BOOL bWroteSomething;
  305.     do {
  306.         bWroteSomething = FALSE;
  307. if(m_pWriteList->GetCount() <= 0)
  308. {
  309.     if(!pAudioOutHdr)
  310. return  RA_AOE_NOERR;
  311.     
  312.     pData = (UCHAR*)pAudioOutHdr->pData->GetBuffer();
  313.     adBuffer._length = pAudioOutHdr->pData->GetSize();
  314. }
  315. else
  316. {
  317.     if (pAudioOutHdr)
  318.     {
  319. IHXBuffer* pNewBuffer = new CHXBuffer();
  320. pNewBuffer->AddRef();
  321. pNewBuffer->Set(pAudioOutHdr->pData->GetBuffer(),
  322. pAudioOutHdr->pData->GetSize());
  323. m_pWriteList->AddTail(pNewBuffer);
  324.     }
  325.     pBuffer = (IHXBuffer*)m_pWriteList->RemoveHead();
  326.     HX_ASSERT(pBuffer);
  327.     pData = pBuffer->GetBuffer();
  328.     adBuffer._length  = pBuffer->GetSize();
  329. }
  330.     
  331.         // Write audio data to device.
  332.         long count = 0;
  333.         int         wrote;
  334.     
  335.         adBuffer._buffer = pData;
  336.         adBuffer._maximum = adBuffer._length;
  337.     
  338.         int     nBytesPerSample = (m_bits_per_sample/8)* m_num_channels;
  339.         long    lSamplesToWrite = adBuffer._length / nBytesPerSample;
  340.         long    lSamplesWritten =0;    
  341.     
  342.         int     transferredCount = adBuffer._length;
  343.         m_wLastError = RA_AOE_NOERR;  // less work this way.
  344.         UMSAudioDevice_ReturnCode retCode = 
  345.             m_pAudioDevice->write( gpSomEnvironment, 
  346.  &adBuffer,
  347.  lSamplesToWrite,
  348.  &lSamplesWritten );
  349.         switch(retCode)
  350.         {
  351.             case UMSAudioDevice_Success :    
  352.     {
  353.         int nBytesWritten = lSamplesWritten * nBytesPerSample;
  354.         m_ulTotalWritten += nBytesWritten;
  355.       
  356.       HX_ASSERT(lSamplesWritten <= lSamplesToWrite);
  357.         if( lSamplesWritten == lSamplesToWrite ) 
  358.         {
  359.        bWroteSomething = TRUE;
  360.         }
  361.         else 
  362.                 {  
  363.          /* requeue the balance. */
  364.     long lSamplesToRequeue = lSamplesToWrite - lSamplesWritten;
  365.     IHXBuffer* pNewBuffer = new CHXBuffer();
  366.     pNewBuffer->AddRef();
  367.     pNewBuffer->Set( adBuffer._buffer + nBytesWritten,
  368.  adBuffer._length - nBytesWritten );
  369.     m_pWriteList->AddHead(pNewBuffer);
  370.         }
  371.     }
  372.     break;
  373.     
  374.     case UMSAudioDevice_DeviceError : // indicates EWOULDBLOCK
  375.     case UMSAudioDevice_Preempted :
  376.     case UMSAudioDevice_Interrupted :
  377.             {
  378.         // just requeue the data
  379.         IHXBuffer* pNewBuffer = new CHXBuffer();
  380.         pNewBuffer->AddRef();
  381.         pNewBuffer->Set( adBuffer._buffer, adBuffer._length );
  382.         m_pWriteList->AddHead(pNewBuffer);
  383.         break;
  384.             }
  385.     
  386.     default :  // failure!
  387.         HX_ASSERT(FALSE);
  388.         m_wLastError = UMSErrorCodeToRACode( retCode );
  389.         }  
  390.     
  391.        INT32 newRefCount = 0;
  392.        if( pBuffer ) 
  393.            newRefCount = pBuffer->Release();
  394. HX_ASSERT(newRefCount == 0);
  395. pBuffer = NULL;
  396.        pAudioOutHdr = NULL; // don't add the same buffer again...
  397.     } while (bWroteSomething);
  398.     
  399.     return m_wLastError;
  400. }
  401. // Seek() is never called.
  402. HX_RESULT CAudioOutAIX::_Imp_Seek(ULONG32 ulSeekTime)
  403. {
  404.     return HXR_OK;
  405. }
  406. HX_RESULT CAudioOutAIX::_Imp_Pause()
  407. {
  408.     m_paused = TRUE;
  409.     
  410.     return RA_AOE_NOERR;
  411. }
  412. HX_RESULT CAudioOutAIX::_Imp_Resume()
  413. {
  414.     m_paused = FALSE;
  415.     return RA_AOE_NOERR;
  416. }
  417. /////////////////////////////////////////////////////////////////////////
  418. // Imp_Reset()
  419. //    Reset the device.  It is important to note that this call results 
  420. //    from a seek, so we must act accordingly.
  421. //
  422. HX_RESULT CAudioOutAIX::_Imp_Reset()
  423. {
  424.     _Imp_Close();
  425.     _Imp_Open(NULL);
  426.     while(!m_pWriteList->IsEmpty())
  427.       {
  428. IHXBuffer* pBuffer = (IHXBuffer*)m_pWriteList->RemoveHead();
  429. HX_RELEASE(pBuffer);
  430.       }
  431.     HX_ASSERT( m_pWriteList->IsEmpty() );
  432.     // these are vital! If they are not set this way, playback after a seek 
  433.     // will just stop after a few frames.
  434.     m_bPaused       = FALSE;
  435.     m_bFirstWrite   = TRUE;
  436.     // and these are fairly obvious, but necessary after a seek. It should be
  437.     // noted here that the audio timeline after a seek is relative to the 
  438.     // internal timeline of the core scheduler; all our playback is based from 
  439.     // our start.
  440.     m_ulTotalWritten =0;
  441.     m_ulCurrentTime = 0;
  442.     
  443.     return RA_AOE_NOERR;
  444. }
  445. // this will force the player to play all remaining data.  The call
  446. // will block until finished playing - if this is not what is desired,
  447. // change the second parameter of the call to FALSE. 
  448. HX_RESULT CAudioOutAIX::_Imp_Drain()
  449. {
  450.     // -pjg need to drain the WriteList also.
  451.     // this will force the player to play all remaining data.  If passed TRUE, 
  452.     // the call will block until finished playing. 
  453.     while(!m_pWriteList->IsEmpty())
  454.       {
  455. IHXBuffer* pBuffer = (IHXBuffer*)m_pWriteList->RemoveHead();
  456. HX_RELEASE(pBuffer);
  457.       }
  458.     m_pAudioDevice->play_remaining_data( gpSomEnvironment, TRUE );
  459.     
  460.     return RA_AOE_NOERR;
  461. }
  462. AUDIOERROR CAudioOutAIX::SetDeviceConfig( const HXAudioFormat* pFormat )
  463. {
  464.     m_pAudioDevice->set_sample_rate        ( gpSomEnvironment, 
  465.      pFormat->ulSamplesPerSec, 
  466.      &m_oSamples );
  467.     m_pAudioDevice->set_number_of_channels ( gpSomEnvironment, 
  468.      pFormat->uChannels );
  469.     m_pAudioDevice->set_bits_per_sample    ( gpSomEnvironment, 
  470.      pFormat->uBitsPerSample );
  471.     m_pAudioDevice->set_audio_format_type  ( gpSomEnvironment, "PCM" );
  472.     m_pAudioDevice->set_number_format      ( gpSomEnvironment, 
  473.      "TWOS_COMPLEMENT" );
  474.     m_pAudioDevice->set_byte_order         ( gpSomEnvironment, "MSB" ); 
  475.     m_pAudioDevice->set_time_format        ( gpSomEnvironment, 
  476.      UMSAudioTypes_Msecs ); 
  477.     
  478.     m_pAudioDevice->set_balance   ( gpSomEnvironment, lDefaultBalance );  
  479.     m_pAudioDevice->set_volume    ( gpSomEnvironment, zlCurrentVolume );
  480.     m_pAudioDevice->enable_output ( gpSomEnvironment, "LINE_OUT", 
  481.     &m_lLeftGain, &m_lRightGain  );
  482.     
  483.     m_pAudioDevice->initialize    ( gpSomEnvironment );
  484.     
  485.     m_pAudioDevice->start         ( gpSomEnvironment );
  486.     
  487.     m_num_channels = pFormat->uChannels;
  488.     m_sample_rate  = pFormat->ulSamplesPerSec; 
  489.     m_bits_per_sample = pFormat->uBitsPerSample;
  490.     
  491.     return RA_AOE_NOERR;
  492. }
  493. HX_RESULT CAudioOutAIX::_Imp_CheckFormat( const HXAudioFormat* pFormat )
  494. {
  495.     // Check for valid format inputs.
  496.     if ( pFormat->uChannels != 1 && pFormat->uChannels != 2 ) 
  497.       {
  498. return HXR_FAIL;
  499.       }
  500.     if ( pFormat->uBitsPerSample != 8 && pFormat->uBitsPerSample != 16 ) 
  501.       {
  502. return HXR_FAIL;
  503.       }
  504.     
  505.     return HXR_OK;
  506. }
  507. /************************************************************************
  508.  *  Method:
  509.  *              CAudioOutAIX::_Imp_GetCurrentTime
  510.  *      Purpose:
  511.  *              Get the current time from the audio device.
  512.  * We added this to support the clock available in the
  513.  * Window's audio driver.
  514.  *  
  515.  *              Current time is simply a function of the total number of 
  516.  *              bytes written to the device minus the number still in
  517.  *              the device queue.  This is expressed as playbackbytes.
  518.  *
  519.  *              Ported to Aix 4.3.1 by fhutchinson, Jan 1999.
  520.  *
  521.  *   The minitimer below is a simple timer employed to avoid excessive 
  522.  *   calls to the UMS audio driver to get the current time, the rationale 
  523.  *   being that a gettimeofday call and a little arithmetic is less expensive 
  524.  *   than a call to the driver. The threshold value is the number of 
  525.  *   microseconds (1/1,000,000 sec) that must pass before the time goes off.
  526.  */    
  527. #include <string.h>  // for strerror()
  528. HX_RESULT CAudioOutAIX::_Imp_GetCurrentTime( ULONG32& ulCurrentTime )
  529. {
  530.     long lBytesPerSample = m_bits_per_sample/8;
  531.     long lBytesStillInDeviceBuffer;
  532.     ulCurrentTime = m_ulCurrentTime;
  533.     UMSAudioDevice_ReturnCode retCode;
  534.     
  535.     // set the device to return the amount of data still in the device buffer as a
  536.     // number of bytes
  537.     retCode = m_pAudioDevice->set_time_format( gpSomEnvironment, UMSAudioTypes_Bytes );
  538.     if( retCode != UMSAudioDevice_Success ) 
  539.     {
  540. m_wLastError = UMSErrorCodeToRACode( retCode );
  541. return (m_wLastError = UMSErrorCodeToRACode( retCode ) );
  542.     }
  543.     
  544.     retCode = m_pAudioDevice->write_buff_used( gpSomEnvironment, &lBytesStillInDeviceBuffer );
  545.     if( retCode != UMSAudioDevice_Success ) 
  546.     {
  547. // almost certainly because the device is not opened.
  548. return (m_wLastError = UMSErrorCodeToRACode( retCode ) );
  549.     }
  550.     
  551.     long lBytesPlayed =  m_ulTotalWritten - lBytesStillInDeviceBuffer;
  552.     
  553.     m_ulCurrentTime = ulCurrentTime = 
  554.       ( (lBytesPlayed /lBytesPerSample) / 
  555. (float)m_sample_rate * 1000 / m_num_channels );
  556.    
  557.     return HXR_OK;
  558. }
  559. /************************************************************************
  560.  *  Method:
  561.  *              CAudioOutAIX::_Imp_GetAudioFd
  562.  *      Purpose: Intended to return the file descriptor of the device. 
  563.  *               However, UMS is not file descriptor-based, so we 
  564.  *               return a -1, generally accepted as a closed device fd.
  565.  */
  566. INT16 CAudioOutAIX::_Imp_GetAudioFd()
  567. {
  568.     return -1;
  569. }
  570. /************************************************************************
  571.  *  Method:
  572.  *              CAudioOutAIX::DoTimeSyncs
  573.  *      Purpose:
  574.  * Manual time syncs! Fork!
  575.  */
  576. void CAudioOutAIX::DoTimeSyncs()
  577. {
  578.     ReschedPlaybackCheck();
  579.     OnTimeSync(); // XXXDMB      // hxaudev.cpp::CHXAudioDevice::OnTimeSync()
  580.     
  581.     return;
  582. }
  583. /************************************************************************
  584.  *  Method:
  585.  *              CAudioOutAIX::ReschedPlaybackCheck()
  586.  *      Purpose:
  587.  * Reschedule playback callback.
  588.  */
  589. HX_RESULT CAudioOutAIX::ReschedPlaybackCheck()
  590. {
  591.     HX_RESULT theErr = HXR_OK;
  592.     
  593.     if (m_bCallbackPending)
  594.       return theErr;
  595.     *m_pPlaybackCountCBTime += (int) (m_ulGranularity*1000) / 2;
  596.     // Put this back in the scheduler.
  597.     HXPlaybackCountCb* pCallback = 0;
  598.     pCallback = new HXPlaybackCountCb(TRUE);
  599.     if (pCallback)
  600.       {
  601. pCallback->m_pAudioDeviceObject = this;
  602. m_bCallbackPending = TRUE;
  603. m_PendingCallbackID = 
  604.   m_pScheduler->AbsoluteEnter(pCallback,*((HXTimeval*)m_pPlaybackCountCBTime)) ;
  605.       }
  606.     else
  607.       theErr = HXR_OUTOFMEMORY;  // but ignored, why?
  608.     
  609.     return theErr;
  610. }
  611. UINT16 CAudioOutAIX::_NumberOfBlocksRemainingToPlay(void)
  612. {
  613.     ULONG32 ulCurTime = 0;
  614.     GetCurrentAudioTime(ulCurTime);
  615.     
  616.     UINT32 bytesBuffered = 0;
  617.     LISTPOSITION i = m_pWriteList->GetHeadPosition();
  618.     
  619.     while (i)
  620.     {
  621. bytesBuffered += ((IHXBuffer *)m_pWriteList->GetAt(i)) -> GetSize();
  622. m_pWriteList->GetNext(i);
  623.     }
  624.     m_pAudioDevice->set_time_format( gpSomEnvironment, UMSAudioTypes_Bytes );
  625.     long lBytesStillInDeviceBuffer;
  626.     UINT32 tries = 100;
  627.     UMSAudioDevice_ReturnCode retCode;
  628.     while (--tries &&
  629.            (m_pAudioDevice->write_buff_used( gpSomEnvironment, &lBytesStillInDeviceBuffer )) != UMSAudioDevice_Success);
  630.     if (!tries)
  631.     {
  632. HX_ASSERT(FALSE);
  633. // don't know what to do!
  634.     }
  635.     UINT16 blocks = 
  636. (bytesBuffered + lBytesStillInDeviceBuffer) / m_wBlockSize + 1;
  637.     //UINT16 blocks = (int) (((double)bytesBuffered + lBytesStillInDeviceBuffer) /
  638. //      m_ulBytesPerGran) + 1;
  639.     return blocks;
  640. }
  641. // CAudioOutAIX::HXPlaybackCountCb
  642. CAudioOutAIX::HXPlaybackCountCb::HXPlaybackCountCb(BOOL timed) 
  643.   : m_lRefCount (0),
  644.     m_pAudioDeviceObject (0),
  645.     m_timed(timed)
  646. {
  647. }
  648. CAudioOutAIX::HXPlaybackCountCb::~HXPlaybackCountCb()
  649. {
  650. }
  651. /*
  652.  * IUnknown methods
  653.  */
  654. /////////////////////////////////////////////////////////////////////////
  655. //      Method:
  656. //              IUnknown::QueryInterface
  657. //      Purpose:
  658. //              Implement this to export the interfaces supported by your 
  659. //              object.
  660. //
  661. STDMETHODIMP CAudioOutAIX::HXPlaybackCountCb::QueryInterface(REFIID riid, void** ppvObj)
  662. {
  663.     if (IsEqualIID(riid, IID_IHXCallback))
  664.     {
  665.         AddRef();
  666.         *ppvObj = (IHXCallback*)this;
  667.         return HXR_OK;
  668.     }
  669.     else if (IsEqualIID(riid, IID_IUnknown))
  670.     {
  671.         AddRef();
  672.         *ppvObj = this;
  673.         return HXR_OK;
  674.     }
  675.     
  676.     *ppvObj = NULL;
  677.     return HXR_NOINTERFACE;
  678. }
  679. /////////////////////////////////////////////////////////////////////////
  680. //      Method:
  681. //              IUnknown::AddRef
  682. //      Purpose:
  683. //              Everyone usually implements this the same... feel free to use
  684. //              this implementation.
  685. //
  686. STDMETHODIMP_(ULONG32) CAudioOutAIX::HXPlaybackCountCb::AddRef()
  687. {
  688.   return InterlockedIncrement(&m_lRefCount);
  689. }
  690. /////////////////////////////////////////////////////////////////////////
  691. //      Method:
  692. //              IUnknown::Release
  693. //      Purpose:
  694. //              Everyone usually implements this the same... feel free to use
  695. //              this implementation.
  696. //
  697. STDMETHODIMP_(ULONG32) CAudioOutAIX::HXPlaybackCountCb::Release()
  698. {
  699.     if (InterlockedDecrement(&m_lRefCount) > 0)
  700.     {
  701.         return m_lRefCount;
  702.     }
  703.     delete this;
  704.     return 0;
  705. }
  706. /*
  707.  *      IHXPlaybackCountCb methods
  708.  */
  709. STDMETHODIMP CAudioOutAIX::HXPlaybackCountCb::Func(void)
  710. {
  711.     if (m_pAudioDeviceObject)
  712.     {
  713. if(!m_timed)
  714. {
  715.     m_pAudioDeviceObject->_Imp_Write(NULL);
  716. }
  717. else
  718. {
  719.     m_pAudioDeviceObject->_Imp_Write(NULL);
  720.     m_pAudioDeviceObject->m_bCallbackPending  = FALSE;
  721.     m_pAudioDeviceObject->DoTimeSyncs();
  722. }
  723.     }
  724.     return HXR_OK;
  725. }
  726. //
  727. //   getUMSAudioDeviceError()
  728. //  
  729. //  returns the string description of the given UMS error code
  730. //
  731. static const char * getUMSAudioDeviceError( UMSAudioDevice_ReturnCode code ) 
  732. {
  733.   switch( code )
  734.     {
  735.     case UMSAudioDevice_Success        : return "Success";
  736.     case UMSAudioDevice_InvalidParam   : return "Invalid parameter";
  737.     case UMSAudioDevice_MemoryError    : return "Success";
  738.     case UMSAudioDevice_DeviceNotAvail : return "Device is not available";
  739.     case UMSAudioDevice_Preempted      : return "Preempted";
  740.     case UMSAudioDevice_Interrupted    : return "Inerrupted";
  741.     case UMSAudioDevice_DeviceError    : return "Device error";
  742.       
  743.       // make sure this is last before default case.
  744.     case UMSAudioDevice_Failure : return "undescribed error";
  745.     default : break;
  746.       
  747.     }
  748.   return "Unknown error";
  749. }
  750. //
  751. //  UMSErrorCodeToRACode()
  752. // 
  753. //  Converts the UMS return code to a best-guess RMA return code equivalent.
  754. //
  755. audio_error UMSErrorCodeToRACode( UMSAudioDevice_ReturnCode retCode ) 
  756. {
  757.   switch( retCode )
  758.     {
  759.     case UMSAudioDevice_Success               : return RA_AOE_NOERR;
  760.     case UMSAudioDevice_InvalidParam          : return RA_AOE_INVALPARAM;
  761.     case UMSAudioDevice_DeviceNotAvail        : return RA_AOE_BADDEVICEID;
  762.     case UMSAudioDevice_NoDevice              : return RA_AOE_BADDEVICEID; 
  763.     case UMSAudioDevice_IncompatibleSettings  : return RA_AOE_BADFORMAT  ; 
  764.     case UMSAudioDevice_NotOpen               : return RA_AOE_DEVNOTOPEN  ; 
  765.     case UMSAudioDevice_NotReady              : return RA_AOE_DEVBUSY  ; 
  766.     case UMSAudioDevice_SettingsChanged       : return RA_AOE_BADFORMAT  ; 
  767.       // this is a stretch...
  768.     case UMSAudioDevice_DeviceError           : return RA_AOE_BADWRITE  ; 
  769.     case UMSAudioDevice_NotSupported          : return RA_AOE_NOTSUPPORTED  ; 
  770.       // and these we will classify under general for now.
  771.     case UMSAudioDevice_Interrupted           : ;
  772.     case UMSAudioDevice_Preempted             : ;
  773.     case UMSAudioDevice_MemoryError           : ;
  774.     case UMSAudioDevice_Failure               : ;
  775.     case UMSAudioDevice_UnderRun              : ;
  776.     case UMSAudioDevice_OverRun               : ;
  777.     default : break;
  778.     }
  779.   return RA_AOE_GENERAL;
  780. }