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

Symbian

开发平台:

Visual C++

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