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

Symbian

开发平台:

Visual C++

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Source last modified: $Id: audSolaris.cpp,v 1.4.8.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. #include "audSolaris.h"
  50. #include "ihxpckts.h"
  51. #include "hxstrutl.h"
  52. #include <unistd.h>
  53. #include <fcntl.h>
  54. #include <stdlib.h>
  55. #include <errno.h>
  56. #include <sys/ioctl.h>
  57. #include <sys/stropts.h> //for I_FLUSH
  58. #include <stdio.h>
  59. //------------------------------------------
  60. // Ctors and Dtors.
  61. //------------------------------------------
  62. CAudioOutSolaris::CAudioOutSolaris() :
  63.     CAudioOutUNIX(),
  64.     m_nDevID(NO_FILE_DESCRIPTOR),
  65.     m_nMixerID(NO_FILE_DESCRIPTOR),
  66.     m_nBlockDivisions(2)
  67. {
  68. };
  69. CAudioOutSolaris::~CAudioOutSolaris()
  70. {
  71.     //The mixer may be used without the audio device. Make sure
  72.     //it gets closed here.
  73.     _CloseMixer();
  74. };
  75. //-------------------------------------------------------
  76. // These Device Specific methods must be implemented 
  77. // by the platform specific sub-classes.
  78. //-------------------------------------------------------
  79. INT16 CAudioOutSolaris::_Imp_GetAudioFd(void)
  80. {
  81.     return m_nDevID;
  82. }
  83. //Devic specific method to set the audio device characteristics. Sample rate,
  84. //bits-per-sample, etc.
  85. //Method *must* set member vars. m_unSampleRate and m_unNumChannels.
  86. HX_RESULT CAudioOutSolaris::_SetDeviceConfig( const HXAudioFormat* pFormat )
  87. {
  88.     HX_RESULT  retCode = RA_AOE_NOERR;    
  89.     audio_info stAudioinfo;
  90.         
  91.     if( m_nDevID < 0 )
  92.     {
  93.         retCode = RA_AOE_DEVNOTOPEN;
  94.     }
  95.     else
  96.     {
  97.         AUDIO_INITINFO( &stAudioinfo );
  98.         stAudioinfo.play.sample_rate      = pFormat->ulSamplesPerSec;
  99.         stAudioinfo.record.sample_rate    = pFormat->ulSamplesPerSec;
  100.         stAudioinfo.play.channels         = pFormat->uChannels;
  101.         stAudioinfo.record.channels       = pFormat->uChannels;
  102.         stAudioinfo.play.precision        = pFormat->uBitsPerSample;
  103.         stAudioinfo.record.precision      = pFormat->uBitsPerSample;
  104.         stAudioinfo.play.encoding         = AUDIO_ENCODING_LINEAR;
  105.         stAudioinfo.record.encoding       = AUDIO_ENCODING_LINEAR;
  106. //            stAudioinfo.play.buffer_size      = m_ulBytesPerGran;
  107.             //Now set the new config.
  108.         if( ::ioctl(m_nDevID, AUDIO_SETINFO, &stAudioinfo) < 0 ) 
  109.         {
  110. #ifdef _DEBUG                
  111.             fprintf( stderr, "Error setting audio confign");
  112. #endif                
  113.             retCode = RA_AOE_GENERAL;
  114.         }
  115.         else
  116.         {
  117.             //Set member vars from CAudioOutUNIX.
  118.             m_wBlockSize         = m_ulBytesPerGran;
  119.             m_unNumChannels      = pFormat->uChannels;
  120.             m_uSampFrameSize     = pFormat->uBitsPerSample / 8;
  121.             m_unSampleRate       = pFormat->ulSamplesPerSec;
  122.             //XXXgfw Is there a size? Doesn't look like it.
  123.             //XXXgfw Lets just pick one that looks good.
  124.             m_ulDeviceBufferSize = 1<<16;
  125.         }
  126.             
  127.         AUDIO_INITINFO(&stAudioinfo);
  128.         if( ::ioctl(m_nDevID, AUDIO_GETINFO, &stAudioinfo) < 0 ) 
  129.         {
  130. #ifdef _DEBUG            
  131.             fprintf( stderr, "CAudioOutSolaris: Can't get audio configuration.n");
  132. #endif            
  133.             retCode = RA_AOE_GENERAL;
  134.         }
  135.         else
  136.         {
  137.             HX_ASSERT( stAudioinfo.play.sample_rate == m_unSampleRate ); 
  138.             HX_ASSERT( stAudioinfo.play.channels    == m_unNumChannels );
  139.             HX_ASSERT( stAudioinfo.play.precision   == m_uSampFrameSize*8 );
  140.             HX_ASSERT( stAudioinfo.play.encoding    == AUDIO_ENCODING_LINEAR );
  141. //                HX_ASSERT( stAudioinfo.play.buffer_size == m_ulBytesPerGran );
  142.         }
  143.     }
  144.     //XXXgfw 
  145. //      fprintf( stderr, "channels: %d   FrameSize: %d   SampleRate: %d   BuffSize: %d BlockSize: %dn",
  146. //               m_unNumChannels, m_uSampFrameSize, m_unSampleRate, m_ulDeviceBufferSize, m_wBlockSize);
  147.     m_wLastError = retCode;
  148.     return m_wLastError;
  149. }
  150. ULONG32 CAudioOutSolaris::_WriteWithEOF(UCHAR* buffer, ULONG32 ulBuffLength )
  151. {
  152.     HX_ASSERT( ulBuffLength != 0 );
  153.     
  154.     ULONG32 lCount = ::write( m_nDevID, buffer, ulBuffLength);
  155.     if( lCount > 0 )
  156.     {
  157.         //We wrote at least one byte. Write an EOF marker.
  158.         char  szEOF[1]; /* Flawfinder: ignore */
  159.         if( ::write( m_nDevID, szEOF, 0) < 0 )
  160.         {
  161.             //Some sort of error. This is going to throw off our
  162.             //ability to report the number of bytes played. We could
  163.             //keep track of how many times this happens and then just
  164.             //force a restart of the playback or something.
  165.             HX_ASSERT( "_WriteWithEOF: Failed to write EOF" == NULL );
  166.         }
  167.     }
  168.     else
  169.     {
  170.         HX_ASSERT( "_WriteWithEOF: Can't write to device." == NULL );
  171.     }
  172.     return lCount;
  173. }
  174. //Device specific method to write bytes out to the audiodevice and return a 
  175. //count of bytes written. 
  176. HX_RESULT CAudioOutSolaris::_WriteBytes( UCHAR* buffer, ULONG32 ulBuffLength, LONG32& lCount )
  177. {
  178.     HX_ASSERT( ulBuffLength==m_wBlockSize);
  179.     
  180.     HX_RESULT retCode = RA_AOE_NOERR;
  181.     if( m_nDevID < 0 )
  182.     {
  183.         retCode = RA_AOE_DEVNOTOPEN;
  184.     }
  185.     else if( ulBuffLength > 0 ) //Ignore all zero length writes. They screw up eof count.
  186.     {
  187.         //Try to increase resolution by splitting the write. ulBuffLength is 
  188.         //always even.
  189.         HX_ASSERT( (m_wBlockSize%m_nBlockDivisions) == 0 );
  190.         
  191.         int i=0;
  192.         int nChunkSize = m_wBlockSize/m_nBlockDivisions;
  193.         
  194.         for( i=0 ; i< m_nBlockDivisions ; i++ )
  195.         {
  196.             lCount = _WriteWithEOF( buffer+i*nChunkSize, nChunkSize );
  197.             if( lCount < 0 )
  198.                 break;
  199.         }
  200.         
  201.         if( lCount < 0 )
  202.         {
  203.             //Error occurred.
  204.             if( errno == EAGAIN )
  205.                 retCode = RA_AOE_NOERR;
  206.             if( errno == EINTR )
  207.                 retCode = RA_AOE_DEVBUSY;
  208.         }
  209.         else 
  210.         {
  211.             lCount = m_wBlockSize/m_nBlockDivisions*i;
  212.         }
  213.     }
  214.     
  215.     
  216.     return retCode;
  217. }
  218. //Device specific methods to open/close the mixer and audio devices.
  219. HX_RESULT CAudioOutSolaris::_OpenAudio()
  220. {
  221.     HX_RESULT retCode = RA_AOE_NOERR;
  222.     //Check the environmental variable to let user overide default device.
  223.     char *pszOverrideName = getenv( "AUDIO" ); /* Flawfinder: ignore */
  224.     char szDevName[MAX_DEV_NAME+1]; /* Flawfinder: ignore */
  225.     
  226.     // Use defaults if no environment variable is set.
  227.     if ( pszOverrideName && strlen(pszOverrideName)>0 )
  228.     {
  229.         SafeStrCpy( szDevName, pszOverrideName, MAX_DEV_NAME );
  230.     }
  231.     else
  232.     {
  233.         pszOverrideName = getenv( "AUDIODEV" ); /* Flawfinder: ignore */
  234.         if ( pszOverrideName && strlen(pszOverrideName)>0 )
  235.         {
  236.             SafeStrCpy( szDevName, pszOverrideName, MAX_DEV_NAME );
  237.         }
  238.         else
  239.         {
  240.             SafeStrCpy( szDevName, "/dev/audio", MAX_DEV_NAME);
  241.         }
  242.     }
  243.     szDevName[MAX_DEV_NAME]='';
  244.     
  245.     // Open the audio device if it isn't already open
  246.     if ( m_nDevID < 0 )
  247.     {
  248.         //We open in non block mode first, just to test wether or not
  249.         //someone has the audio device. After that we must reopen it
  250.         //so that our writes always block.
  251.         m_nDevID = ::open( szDevName, O_WRONLY | O_EXCL | O_NONBLOCK );
  252.         if( m_nDevID >= 0 )
  253.         {
  254.             //XXXgfw Yeah, I know, why don't I just use ioctl(....,FIONBIO, ...);
  255.             //or fcntl? because it doesn't work!
  256.             close(m_nDevID);
  257.             m_nDevID = ::open( szDevName, O_WRONLY | O_EXCL);
  258.         }
  259.         
  260.     }
  261.     
  262.     if ( m_nDevID < 0 )
  263.     {
  264.         //Error opening device.
  265.         retCode = RA_AOE_BADOPEN;
  266.     }
  267.     m_wLastError = retCode;
  268.     return m_wLastError;
  269. }
  270. HX_RESULT CAudioOutSolaris::_CloseAudio()
  271. {
  272.     HX_RESULT retCode = RA_AOE_NOERR; 
  273.     
  274.     if( m_nDevID >= 0 )
  275.     {
  276.         ::close( m_nDevID );
  277.         m_nDevID = NO_FILE_DESCRIPTOR;
  278.     }
  279.     else
  280.     {
  281.         retCode = RA_AOE_DEVNOTOPEN;
  282.     }
  283.     m_wLastError = retCode;
  284.     return m_wLastError;
  285. }
  286. HX_RESULT CAudioOutSolaris::_OpenMixer()
  287. {
  288.     HX_RESULT retCode = RA_AOE_NOERR;
  289.     if(!m_bMixerPresent)
  290.     {
  291.         //Let user override default device with environ variable.
  292.         char *pszOverrideName = getenv( "MIXER" ); /* Flawfinder: ignore */
  293.         char szDevCtlName[MAX_DEV_NAME]; /* Flawfinder: ignore */
  294.         
  295.         if (pszOverrideName && strlen(pszOverrideName)>0 )
  296.         {
  297.             SafeStrCpy( szDevCtlName , pszOverrideName, MAX_DEV_NAME );
  298.         }
  299.         else
  300.         {
  301.             SafeStrCpy( szDevCtlName, "/dev/audioctl", MAX_DEV_NAME); // default for volume
  302.         }
  303.         
  304.         m_nMixerID = ::open( szDevCtlName, O_RDWR );
  305.         
  306.         if (m_nMixerID > 0)
  307.         {
  308.             m_bMixerPresent = 1;
  309.             _Imp_GetVolume();
  310.         }
  311.         else
  312.         {
  313.             m_nMixerID = NO_FILE_DESCRIPTOR;
  314.             m_bMixerPresent = 0;
  315.         }
  316.     }
  317.     m_wLastError = retCode;
  318.     return m_wLastError;
  319. }
  320. HX_RESULT CAudioOutSolaris::_CloseMixer()
  321. {
  322.     HX_RESULT retCode = RA_AOE_NOERR;
  323.     if( m_nMixerID >= 0 )
  324.     {
  325.         ::close( m_nMixerID );
  326.         m_nMixerID = NO_FILE_DESCRIPTOR;
  327.     }
  328.     
  329.     m_wLastError = retCode;
  330.     return m_wLastError;
  331. }
  332. //Device specific method to reset device and return it to a state that it 
  333. //can accept new sample rates, num channels, etc.
  334. HX_RESULT CAudioOutSolaris::_Reset()
  335. {
  336.     HX_RESULT retCode = RA_AOE_NOERR;
  337.     
  338.     if ( m_nDevID < 0 )
  339.     {
  340.         retCode = RA_AOE_DEVNOTOPEN;
  341.     }
  342.     else if( ::ioctl(m_nDevID, I_FLUSH, FLUSHW) == -1 )
  343.     {
  344.         retCode = RA_AOE_GENERAL;
  345.     }
  346.     //Now, m_ulTotalWritten is going to go to Zero in CAudioOutUNIX.
  347.     //So, we must reset the EOF marker.
  348.     audio_info stAudioInfo;
  349.     AUDIO_INITINFO( &stAudioInfo );
  350.     
  351.     //Fill in the structure with current settings.
  352.     if (::ioctl(m_nDevID, AUDIO_GETINFO, &stAudioInfo) < 0) 
  353.     {
  354. #ifdef _DEBUG        
  355. fprintf( stderr, "CAudioOutSolaris: Just trying to reset EOF in _Reset.n");
  356. #endif        
  357.     }
  358.     //Just change the gain setting. Must map incoming [0,100]-->[0,255]
  359.     stAudioInfo.play.eof     = 0;
  360.     stAudioInfo.play.samples = 0;
  361.     //Now store the new settings.
  362.     if( ::ioctl(m_nMixerID, AUDIO_SETINFO, &stAudioInfo)<0 ) 
  363.     {
  364. #ifdef _DEBUG        
  365. fprintf( stderr, "CAudioOutSolaris: just trying to reset EOFn");
  366. #endif        
  367.     }
  368.     m_wLastError = retCode;
  369.     return m_wLastError;
  370. }
  371. //Device specific method to get/set the devices current volume.
  372. UINT16 CAudioOutSolaris::_GetVolume() const
  373. {
  374.     UINT16     unVolume = 0;
  375.     audio_info stAudioInfo;
  376.     
  377.     if (::ioctl( m_nMixerID, AUDIO_GETINFO, &stAudioInfo) < 0)
  378.     {
  379.         //What to do here? Should we close the mixer and pretend
  380.         //we don't have one anymore?
  381.         unVolume = m_uCurVolume;
  382.     }
  383.     else
  384.     {
  385.         //Map device specific volume levels to [0,100]. 
  386.         unVolume = (UINT16) (((float)(stAudioInfo.play.gain-AUDIO_MIN_GAIN))*100.0 / (float)(AUDIO_MAX_GAIN-AUDIO_MIN_GAIN) + 0.5);
  387.         
  388.     }
  389.     return unVolume;
  390. }
  391. HX_RESULT CAudioOutSolaris::_SetVolume(UINT16 unVolume)
  392. {
  393.     HX_RESULT  retCode = RA_AOE_NOERR;
  394.     audio_info stAudioInfo;
  395.     //Fill in the structure with current settings.
  396.     if (::ioctl(m_nMixerID, AUDIO_GETINFO, &stAudioInfo) < 0) 
  397.     {
  398. #ifdef _DEBUG        
  399. fprintf( stderr, "CAudioOutSolaris: Can't get the audio configurationn");
  400. #endif        
  401. retCode = RA_AOE_NOTSUPPORTED;
  402.     }
  403.     //Just change the gain setting. Must map incoming [0,100]-->[0,255]
  404.     stAudioInfo.play.gain = (UINT16)((float)unVolume/100.0 *(float)(AUDIO_MAX_GAIN-AUDIO_MIN_GAIN) + AUDIO_MIN_GAIN + 0.5);
  405.     //Now store the new settings.
  406.     if( ::ioctl(m_nMixerID, AUDIO_SETINFO, &stAudioInfo)<0 ) 
  407.     {
  408. #ifdef _DEBUG        
  409. fprintf( stderr, "CAudioOutSolaris: Can't set the audio configuration.n");
  410. #endif        
  411.         retCode = RA_AOE_NOTSUPPORTED;
  412.     }
  413.     
  414.     m_wLastError = retCode;
  415.     return m_wLastError;
  416. }
  417. //Device specific method to drain a device. This should play the remaining
  418. //bytes in the devices buffer and then return.
  419. HX_RESULT CAudioOutSolaris::_Drain()
  420. {
  421.     HX_RESULT retCode = RA_AOE_NOERR;
  422.     
  423.     if ( m_nDevID < 0 )
  424.     {
  425.         retCode = RA_AOE_DEVNOTOPEN;
  426.     }
  427.     //XXXgfw We don't use _Drain anywhere right now. The problem
  428.     //XXXgfw is that drain will block the proc until the buffers
  429.     //XXXgfw are all drained. We don't want that. Just let it
  430.     //XXXgfw play.
  431. //      else if( ::ioctl (m_nDevID, AUDIO_DRAIN, 0) == -1 )
  432. //      {
  433. //          retCode = RA_AOE_GENERAL;
  434. //      }
  435.     m_wLastError = retCode;
  436.     return m_wLastError;
  437. }
  438. UINT64 CAudioOutSolaris::_GetBytesActualyPlayed(void) const
  439. {
  440.     UINT64    ulTheAnswer = 0;
  441.     audio_info stAudioInfo;
  442.     //Fill in the structure with current settings.
  443.     AUDIO_INITINFO( &stAudioInfo );
  444.     if (::ioctl(m_nDevID, AUDIO_GETINFO, &stAudioInfo) < 0) 
  445.     {
  446. #ifdef _DEBUG        
  447. fprintf( stderr, "CAudioOutSolaris::_GetBytesActualyPlayed: Failure.n");
  448. #endif
  449.         HX_ASSERT( "_GetBytesActualyPlayed: Error reading audio device"==NULL );
  450.         ulTheAnswer = 0;
  451.     }
  452.     if( stAudioInfo.play.error )
  453.     {
  454.         //Underflow has occured.
  455.         HX_ASSERT( "_GetBytesActualyPlayed: underflow in audio device"==NULL);
  456.     }
  457.     
  458.     //We *always* right full blocks to the device. Well, in the
  459.     //case of solaris, we don't use the rewind mechinism of CAudioOutUNIX 
  460.     //so this is true.
  461.     ulTheAnswer = stAudioInfo.play.eof*m_wBlockSize/m_nBlockDivisions;
  462.     //XXXgfw It would be so cool of we could count on the samples field.
  463. //    ulTheAnswer = (ULONG32)stAudioInfo.play.samples*m_unNumChannels*m_uSampFrameSize;
  464. //        fprintf( stderr, "m_ulTotalWritten: %lu  Bytesplayed:%lu   eof: %d  samples: %d n",
  465. //                 m_ulTotalWritten, ulTheAnswer, stAudioInfo.play.eof, stAudioInfo.play.samples );
  466.     
  467.     return  ulTheAnswer; 
  468. }
  469. HX_RESULT CAudioOutSolaris::_GetRoomOnDevice(ULONG32& ulBytes) const 
  470. {
  471.     HX_RESULT      retCode = RA_AOE_NOERR;
  472.     
  473.     ulBytes = m_ulDeviceBufferSize-(m_ulTotalWritten-_GetBytesActualyPlayed());
  474.     m_wLastError = retCode;
  475.     return m_wLastError;
  476. }
  477. HX_RESULT CAudioOutSolaris::_CheckFormat( const HXAudioFormat* pFormat )
  478. {
  479.     m_wLastError = RA_AOE_NOERR;
  480.     return m_wLastError;
  481. }
  482. HX_RESULT CAudioOutSolaris::_CheckSampleRate( ULONG32 ulSampleRate ) 
  483. {
  484.     m_wLastError = RA_AOE_NOERR;
  485.     //XXXgfw For now, just assume that it is supported.
  486.     
  487.     return m_wLastError;
  488. }
  489. HX_RESULT CAudioOutSolaris::_Pause() 
  490. {
  491.     audio_info_t stAudioInfo;
  492.     m_wLastError = HXR_OK;
  493.     AUDIO_INITINFO(&stAudioInfo);
  494.     stAudioInfo.play.pause = 1;
  495.     if( ::ioctl(m_nDevID, AUDIO_SETINFO, &stAudioInfo) < 0 )
  496.     {
  497.         m_wLastError = RA_AOE_NOTSUPPORTED;
  498.     }
  499.     return m_wLastError;
  500. }
  501. HX_RESULT CAudioOutSolaris::_Resume()
  502. {
  503.     audio_info_t stAudioInfo;
  504.     m_wLastError = HXR_OK;
  505.     AUDIO_INITINFO(&stAudioInfo);
  506.     stAudioInfo.play.pause = 0;
  507.     if( ::ioctl(m_nDevID, AUDIO_SETINFO, &stAudioInfo) < 0 ) 
  508.     {
  509.         m_wLastError = RA_AOE_NOTSUPPORTED;
  510.     }
  511.     return m_wLastError;
  512. }
  513. BOOL CAudioOutSolaris::_IsSelectable() const
  514. {
  515.     return FALSE;
  516. }
  517. BOOL CAudioOutSolaris::_HardwarePauseSupported() const
  518. {
  519.     return TRUE;
  520. }
  521.