www.pudn.com > helix.src.0812.rar > audlinux_oss.cpp


/* ***** BEGIN LICENSE BLOCK *****  
 * Version: RCSL 1.0/RPSL 1.0  
 *   
 * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved.  
 *       
 * The contents of this file, and the files included with this file, are  
 * subject to the current version of the RealNetworks Public Source License  
 * Version 1.0 (the "RPSL") available at  
 * http://www.helixcommunity.org/content/rpsl unless you have licensed  
 * the file under the RealNetworks Community Source License Version 1.0  
 * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl,  
 * in which case the RCSL will apply. You may also obtain the license terms  
 * directly from RealNetworks.  You may not use this file except in  
 * compliance with the RPSL or, if you have a valid RCSL with RealNetworks  
 * applicable to this file, the RCSL.  Please see the applicable RPSL or  
 * RCSL for the rights, obligations and limitations governing use of the  
 * contents of the file.   
 *   
 * This file is part of the Helix DNA Technology. RealNetworks is the  
 * developer of the Original Code and owns the copyrights in the portions  
 * it created.  
 *   
 * This file, and the files included with this file, is distributed and made  
 * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER  
 * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES,  
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS  
 * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.  
 *  
 * Technology Compatibility Kit Test Suite(s) Location:  
 *    http://www.helixcommunity.org/content/tck  
 *  
 * Contributor(s):  
 *   
 * ***** END LICENSE BLOCK ***** */  
 
#include  
#include  
#include  
#include  
#include  
 
#include   
#include   
 
#include "ihxpckts.h" 
#include "hxtick.h" 
#include "hxprefs.h" 
#include "timeval.h" 
#include "hxthread.h" 
#include "audlinux_oss.h" 
#include "hxstrutl.h" 
#include "hxprefutil.h" 
 
//we can't set the PCM volume on the PowerPC. The sound driver 
//only lets up set the master volume. 
#ifdef __powerpc__ 
#   define HX_VOLUME  SOUND_MIXER_VOLUME 
#else 
#   define HX_VOLUME  SOUND_MIXER_PCM 
#endif 
 
 
//------------------------------------------ 
// Ctors and Dtors. 
//------------------------------------------ 
CAudioOutLinux::CAudioOutLinux() : 
    CAudioOutUNIX(), 
    m_ulTickCount(0), 
    m_ulLastBytesPlayed(0), 
    m_ulLastTimeStamp(0), 
    m_ulPausePosition(0), 
    m_nDevID(NO_FILE_DESCRIPTOR), 
    m_nMixerID(NO_FILE_DESCRIPTOR), 
    m_bGetODelayFailed(TRUE), 
    m_bGetOSpaceFailed(FALSE), 
    m_bTestGetODelay(TRUE) 
{ 
}; 
 
CAudioOutLinux::~CAudioOutLinux() 
{ 
    //The mixer is opened independently of the audio device. Make sure  
    //it is closed. 
    _CloseMixer(); 
}; 
 
//------------------------------------------------------- 
// These Device Specific methods must be implemented  
// by the platform specific sub-classes. 
//------------------------------------------------------- 
INT16 CAudioOutLinux::_Imp_GetAudioFd(void) 
{ 
    return m_nDevID; 
} 
 
//Devic specific method to set the audio device characteristics. Sample rate, 
//bits-per-sample, etc. 
//Method *must* set member vars. m_unSampleRate and m_unNumChannels. 
HX_RESULT CAudioOutLinux::_SetDeviceConfig( const HXAudioFormat* pFormat ) 
{ 
    if ( m_nDevID < 0 ) 
        return RA_AOE_DEVNOTOPEN; 
 
    m_wBlockSize = m_ulBytesPerGran; 
 
    //First, we want to determine the fragment size of the device buffer. 
    //We will let the device determine the number of fragments after we 
    //make sure that each fragment is no bigger than our block size. 
    //The minimum block size for OSS is 16 bytes and the max is 2^0xf. 
    //Basically, Log-base-two(m_wBlockSize)+1. 
    int nPower = 0x4; 
    while( (1< 4 ) 
        nPower--; 
     
    //Frag info is 0xMMMMSSSS. Where 
    // 
    // MMMM is the total number of fragments. Set this to 7fff if you want 
    //      OSS to figure it out. 
    // SSSS is the size of each fragment. The size is 2^0xSSSS. 
    int nFragInfo = 0x7fff0000 | nPower; 
 
    //Now set the fragment size. 
    if (ioctl(m_nDevID, SNDCTL_DSP_SETFRAGMENT, &nFragInfo) == -1) 
    { 
        return (m_wLastError = RA_AOE_NOTENABLED); 
    } 
 
    //Now set the format. Either 8-bit or 16-bit audio is supported. 
    int      nSampleWidth  = pFormat->uBitsPerSample; 
    ULONG32  nSampleRate   = pFormat->ulSamplesPerSec; 
    int      numChannels   = pFormat->uChannels; 
    int      nFormat1      = 0; 
    int      nFormat2      = 0; 
     
    if( nSampleWidth == 16) 
    { 
        nFormat1 = nFormat2 = AFMT_S16_NE; 
    } 
    else 
    { 
        nFormat1 = nFormat2 = AFMT_U8; 
    } 
     
    if(ioctl(m_nDevID, SNDCTL_DSP_SETFMT, &nFormat1) == -1) 
    { 
        return (  m_wLastError = RA_AOE_NOTENABLED ); 
    } 
 
    //Check and see if the device supports the format we tried to set. 
    //If it didn't take our only other option is unsigned 8 bit. So, if 
    //that is what the device returned just use it. 
    if(nFormat1!=nFormat2 && nFormat1 != AFMT_U8 ) 
    { 
        //Just try to set AFMT_U8. 
        nFormat1 = AFMT_U8; 
        if(ioctl(m_nDevID, SNDCTL_DSP_SETFMT, &nFormat1) == -1) 
        { 
            return (  m_wLastError = RA_AOE_NOTENABLED ); 
        } 
        if( nFormat1 != AFMT_U8 ) 
        { 
            //No know format is supported. 
            return (  m_wLastError = RA_AOE_NOTENABLED );  
        } 
    } 
    //If we went to 8-bit then  
    if( nFormat1 == AFMT_U8 ) 
    { 
        //AFMT_U8 is set. 
        nSampleWidth = 8; 
    } 
    m_uSampFrameSize = nSampleWidth/8; 
    if ( nSampleWidth != pFormat->uBitsPerSample ) 
    { 
        ((HXAudioFormat*)pFormat)->uBitsPerSample = nSampleWidth; 
    } 
     
     
    // Set sampling rate, sample width, #channels. 
    // 
    // Make sure you set num channels before rate. If you don't it will 
    // screw up SBPro  devices. Your rate will actually be 1/2 of what 
    // you think it is. 
 
    //Set number of channels. Stereo or mono. 
    if (ioctl(m_nDevID, SOUND_PCM_WRITE_CHANNELS, &numChannels) == -1) 
    { 
        return ( m_wLastError = RA_AOE_NOTENABLED ); 
    } 
    m_unNumChannels = numChannels; 
    if ( numChannels != pFormat->uChannels ) 
    { 
        ((HXAudioFormat*)pFormat)->uChannels = numChannels; 
    } 
 
    //Set the sample rate. 
    if (ioctl(m_nDevID, SOUND_PCM_WRITE_RATE, &nSampleRate) == -1) 
    { 
        return ( m_wLastError = RA_AOE_NOTENABLED ); 
    } 
 
    if (nSampleRate == 0) 
    { 
	/*  
	 * Some drivers actually set the sample rate on the device, but 
	 * return 0 for the sample rate. On these platforms we just ignore 
	 * the return value and assume the sample rate is set to what was 
	 * requested. 
	 */ 
 
	nSampleRate = pFormat->ulSamplesPerSec; 
    } 
 
    m_unSampleRate = nSampleRate; 
    if ( nSampleRate != pFormat->ulSamplesPerSec ) 
    { 
        ((HXAudioFormat*)pFormat)->ulSamplesPerSec = nSampleRate; 
    } 
 
 
 
    //Find out if the user wants to support old OSS drivers that don't have, 
    //or support well, the iocls needed for good syncing.  SNDCTL_DSP_GETOSPACE and 
    //SNDCTL_DSP_GETODELAY. 
    IHXPreferences* pPreferences = NULL; 
    if( m_pContext && HXR_OK == m_pContext->QueryInterface( IID_IHXPreferences, (void **) &pPreferences)) 
    { 
	UINT32 nOldOSS = 0; 
	if (HXR_OK == ReadPrefINT32(pPreferences, "SoundDriver", nOldOSS) && 
	    (nOldOSS == 1)) 
	{ 
	    m_bGetODelayFailed = TRUE; 
	    m_bGetOSpaceFailed = TRUE; 
	    m_bTestGetODelay = FALSE; 
	} 
        HX_RELEASE( pPreferences ); 
    } 
 
//for now, PowerPC linux doesn't support the following ioctl call 
//So, I will just make up some buffer size and live with it for 
//now. You won't be able to tell the difference until you turn 
//off threaded audio, then it will be block city! 
#ifndef __powerpc__   
    audio_buf_info getYourInfoHere; 
    if( !m_bGetOSpaceFailed ) 
    { 
        //This call is to get how the device is set up after we have 
        //set the sample rate etc. We use this to set up the buffers 
        //we use for the fake pause/resume features. 
        if (ioctl(m_nDevID, SNDCTL_DSP_GETOSPACE, &getYourInfoHere) == -1)  
        { 
            m_wLastError = RA_AOE_NOTENABLED; 
            return m_wLastError; 
        } 
        m_ulDeviceBufferSize = getYourInfoHere.fragsize*getYourInfoHere.fragstotal; 
    } 
    else 
    { 
        //We don't have anyway to determine how big the buffer is. 
        //just guess I guess. 
        m_ulDeviceBufferSize = 8192*4; 
    } 
#else 
    m_ulDeviceBufferSize = 8192*4; 
#endif 
 
#ifdef _DEBUG 
    fprintf( stderr, "Device Configured:\n"); 
    fprintf( stderr, "         Sample Rate: %d\n",  m_unSampleRate); 
    fprintf( stderr, "        Sample Width: %d\n",  nSampleWidth); 
    fprintf( stderr, "        Num channels: %d\n",  m_unNumChannels); 
    fprintf( stderr, "          Block size: %d\n",  m_wBlockSize); 
    fprintf( stderr, "  Device buffer size: %lu\n", m_ulDeviceBufferSize); 
    fprintf( stderr, "  Supports GETOSPACE: %d\n",  !m_bGetOSpaceFailed); 
    fprintf( stderr, "  Supports GETODELAY: %d\n",  !m_bGetODelayFailed); 
#endif 
    return RA_AOE_NOERR; 
} 
 
void CAudioOutLinux::_SyncUpTimeStamps(ULONG32 lCount) 
{ 
    int bytes2  = 0; 
    int theErr = -1; 
    if( m_bTestGetODelay || !m_bGetODelayFailed ) 
    { 
        HX_ASSERT(m_nDevID ); 
        theErr = ::ioctl(m_nDevID, SNDCTL_DSP_GETODELAY, &bytes2); 
    } 
    if( theErr != -1) 
    { 
	if (m_bTestGetODelay && (bytes2 != 0)) 
	{ 
	    // We've now seen SNDCTL_DSP_GETODELAY return 
	    // a non-zero value so we know it is working 
	    m_bTestGetODelay = FALSE; 
            m_bGetODelayFailed = FALSE; 
	} 
 
	if (!m_bTestGetODelay) 
	{ 
	    m_ulLastBytesPlayed = (UINT64)(m_ulTotalWritten+lCount-bytes2); 
	    m_ulLastTimeStamp   = GetTickCount(); 
	} 
    } 
    else 
    { 
        //so we don't try it again. 
        m_bGetODelayFailed = TRUE; 
	m_bTestGetODelay = FALSE; 
    } 
} 
 
 
//Device specific method to write bytes out to the audiodevice and return a 
//count of bytes written.  
HX_RESULT CAudioOutLinux::_WriteBytes( UCHAR* buffer, ULONG32 ulBuffLength, LONG32& lCount ) 
{ 
    HX_RESULT retCode = RA_AOE_NOERR; 
 
    if( m_nDevID < 0 ) 
    { 
        retCode = RA_AOE_DEVNOTOPEN; 
    } 
    else 
    { 
        if( m_ulTickCount == 0 ) 
            m_ulTickCount = GetTickCount(); 
 
        lCount = ::write( m_nDevID, buffer, ulBuffLength); 
        if( lCount < 0 ) 
        { 
            //Error occurred. 
            if( errno == EAGAIN ) 
                retCode = RA_AOE_NOERR; 
            if( errno == EINTR ) 
                retCode = RA_AOE_DEVBUSY; 
        } 
        else 
        { 
            _SyncUpTimeStamps(lCount); 
        } 
    } 
    return retCode; 
} 
 
//Device specific methods to open/close the mixer and audio devices. 
HX_RESULT CAudioOutLinux::_OpenAudio() 
{ 
    HX_RESULT retCode = RA_AOE_NOERR; 
 
    //Set the tick count to zero 
    m_ulTickCount       = 0; 
    m_ulLastTimeStamp   = 0; 
    m_ulLastBytesPlayed = 0; 
    m_ulPausePosition   = 0; 
 
    //Check the environmental variable to let user overide default device. 
    char *pszOverrideName = getenv( "AUDIO" ); /* Flawfinder: ignore */ 
    char szDevName[MAX_DEV_NAME]; /* Flawfinder: ignore */ 
     
    // Use defaults if no environment variable is set. 
    if ( pszOverrideName && strlen(pszOverrideName)>0 ) 
    { 
        SafeStrCpy( szDevName, pszOverrideName, MAX_DEV_NAME ); 
    } 
    else 
    { 
        SafeStrCpy( szDevName, "/dev/dsp", MAX_DEV_NAME ); 
    } 
     
    // Open the audio device if it isn't already open 
    if ( m_nDevID < 0 ) 
    { 
        m_nDevID = ::open( szDevName, O_WRONLY ); 
    } 
     
    if ( m_nDevID < 0 ) 
    { 
#ifdef _DEBUG         
        fprintf( stderr, "Failed to open audio(%s)!!!!!!! Code is: %d  errno: %d\n", 
                 szDevName, m_nDevID, errno ); 
#endif         
         
        //Error opening device. 
        retCode = RA_AOE_BADOPEN; 
    } 
     
    m_wLastError = retCode; 
    return m_wLastError; 
} 
 
HX_RESULT CAudioOutLinux::_CloseAudio() 
{ 
    HX_RESULT retCode = RA_AOE_NOERR; 
     
    if( m_nDevID >= 0 ) 
    { 
        ::close( m_nDevID ); 
        m_nDevID = NO_FILE_DESCRIPTOR; 
    } 
    else 
    { 
        retCode = RA_AOE_DEVNOTOPEN; 
    } 
    m_wLastError = retCode; 
    return m_wLastError; 
} 
 
HX_RESULT CAudioOutLinux::_OpenMixer() 
{ 
    HX_RESULT retCode = RA_AOE_NOERR; 
 
    if(!m_bMixerPresent) 
    { 
        //Let user override default device with environ variable. 
        char *pszOverrideName = getenv( "MIXER" ); /* Flawfinder: ignore */ 
        char szDevCtlName[MAX_DEV_NAME]; /* Flawfinder: ignore */ 
         
        if (pszOverrideName && strlen(pszOverrideName)>0 ) 
	{ 
            SafeStrCpy( szDevCtlName , pszOverrideName, MAX_DEV_NAME ); 
	} 
        else 
	{ 
            SafeStrCpy( szDevCtlName , "/dev/mixer", MAX_DEV_NAME );	// default for volume 
	} 
         
        m_nMixerID = ::open( szDevCtlName, O_RDWR ); 
         
        if (m_nMixerID > 0) 
        { 
            m_bMixerPresent = 1; 
            _Imp_GetVolume(); 
        } 
        else 
        { 
            m_nMixerID = NO_FILE_DESCRIPTOR; 
            m_bMixerPresent = 0; 
        } 
    } 
 
    m_wLastError = retCode; 
    return m_wLastError; 
} 
 
HX_RESULT CAudioOutLinux::_CloseMixer() 
{ 
    HX_RESULT retCode = RA_AOE_NOERR; 
 
    if( m_nMixerID >= 0 ) 
    { 
        ::close( m_nMixerID ); 
        m_nMixerID = NO_FILE_DESCRIPTOR; 
    } 
     
    m_wLastError = retCode; 
    return m_wLastError; 
} 
 
//Device specific method to reset device and return it to a state that it  
//can accept new sample rates, num channels, etc. 
HX_RESULT CAudioOutLinux::_Reset() 
{ 
    HX_RESULT retCode = RA_AOE_NOERR; 
 
    m_ulPausePosition = 0; 
     
    if ( m_nDevID < 0 ) 
    { 
        retCode = RA_AOE_DEVNOTOPEN; 
    } 
    else if( ::ioctl (m_nDevID, SOUND_PCM_RESET, 0) == -1 ) 
    { 
        retCode = RA_AOE_GENERAL; 
    } 
 
    m_wLastError = retCode; 
    return m_wLastError; 
} 
 
//Device specific method to get/set the devices current volume. 
UINT16 CAudioOutLinux::_GetVolume() const 
{ 
    int nVolume      = 0; 
    int nRetVolume   = 0; 
    int nLeftVolume  = 0; 
    int nRightVolume = 0; 
     
    if (::ioctl( m_nMixerID, MIXER_READ(HX_VOLUME), &nVolume) < 0) 
    { 
        nRetVolume = 0; 
    } 
    nLeftVolume  = (nVolume & 0x000000ff);  
    nRightVolume = (nVolume & 0x0000ff00) >> 8; 
 
    //Which one to use? Average them? 
    nRetVolume = nLeftVolume ; 
 
    return nRetVolume;  
} 
 
HX_RESULT CAudioOutLinux::_SetVolume(UINT16 unVolume) 
{ 
    HX_RESULT retCode = RA_AOE_NOERR; 
     
    int nNewVolume=0; 
 
    //Set both left and right volumes. 
    nNewVolume = (unVolume & 0xff) | ((unVolume &0xff) << 8); 
     
    if (::ioctl( m_nMixerID, MIXER_WRITE(HX_VOLUME), &nNewVolume) < 0) 
    { 
        retCode = RA_AOE_NOTSUPPORTED; 
    } 
 
    m_wLastError = retCode; 
    return m_wLastError; 
} 
 
//Device specific method to drain a device. This should play the remaining 
//bytes in the devices buffer and then return. 
HX_RESULT CAudioOutLinux::_Drain() 
{ 
    HX_RESULT retCode = RA_AOE_NOERR; 
     
    if ( m_nDevID < 0 ) 
    { 
        retCode = RA_AOE_DEVNOTOPEN; 
    } 
    else if( ::ioctl (m_nDevID, SNDCTL_DSP_SYNC, 0) == -1 ) 
    { 
        retCode = RA_AOE_GENERAL; 
    } 
 
    m_wLastError = retCode; 
    return m_wLastError; 
} 
 
 
 
UINT64 CAudioOutLinux::_GetBytesActualyPlayed(void) const 
{ 
    /* Get current playback position in device DMA. */ 
    int     bytes2 = 0; 
    UINT64  ulTheAnswer = 0; 
 
    //What versions of the linux kernel do we want to support? 
    if( !m_bGetODelayFailed ) 
    { 
        if( m_ulTotalWritten > 0 ) 
        { 
            HX_ASSERT( m_unSampleRate!=0 && m_uSampFrameSize!=0 ); 
            ULONG32 ulTick = GetTickCount(); 
 
            //We need to update the timestamps every so often. 
            //This make sure that if the XServer was blocked, and 
            //we ran dry, that we re-sync up. 
            if( (ulTick-m_ulLastTimeStamp)>200 ) 
            { 
                ((CAudioOutLinux*)this)->_SyncUpTimeStamps(); 
                ulTick = GetTickCount(); 
            } 
             
            ulTheAnswer = (UINT64)(m_ulLastBytesPlayed+ 
            ((float)(ulTick-m_ulLastTimeStamp)* 
             (float)m_unNumChannels/1000.0* 
             m_unSampleRate*m_uSampFrameSize) +0.5 ); 
        } 
    } 
    else 
    { 
        //We will assume that the error is because of an incomplete  
        //implementation of the oss compatible driver. So, just  
        //fake it with time stamps. 
        if( m_ulTotalWritten > 0 ) 
        { 
            ulTheAnswer = (UINT64)((float)(GetTickCount()-m_ulTickCount)*(float)m_unNumChannels/1000.0*m_unSampleRate*m_uSampFrameSize); 
            ulTheAnswer += m_ulPausePosition; 
        } 
    } 
    return  ulTheAnswer; 
} 
 
 
//this must return the number of bytes that can be written without blocking. 
//Don't use SNDCTL_DSP_GETODELAY here as it can't compute that amount 
//correctly. 
HX_RESULT CAudioOutLinux::_GetRoomOnDevice(ULONG32& ulBytes) const 
{ 
    HX_RESULT      retCode = RA_AOE_NOERR; 
    audio_buf_info stBuffInfo; 
    int            theErr=0; 
 
    //What versions of the linux kernel do we want to support? 
    if( m_bGetOSpaceFailed ) 
    { 
        theErr = -1; 
    } 
    else 
    { 
        theErr = ::ioctl(m_nDevID, SNDCTL_DSP_GETOSPACE, &stBuffInfo); 
    } 
 
    if ( theErr != -1 ) 
    { 
        ulBytes = stBuffInfo.bytes; 
    } 
    else 
    { 
        //So we just try it once. 
        m_bGetOSpaceFailed = TRUE; 
        ulBytes = m_ulDeviceBufferSize-(m_ulTotalWritten-_GetBytesActualyPlayed() ); 
    } 
 
    m_wLastError = retCode; 
    return m_wLastError; 
} 
 
HX_RESULT CAudioOutLinux::_CheckFormat( const HXAudioFormat* pFormat ) 
{ 
    int   nBitsPerSample = pFormat->uBitsPerSample; 
    int   ulTmp          = pFormat->ulSamplesPerSec;     
    int   nNumChannels   = pFormat->uChannels; 
    float fTmp           = 0.0; 
 
    HX_RESULT retCode = RA_AOE_NOERR; 
     
    //Is the device already open? 
    if( m_nDevID > 0 || RA_AOE_NOERR != _OpenAudio() ) 
    { 
        retCode = RA_AOE_DEVBUSY; 
        return retCode; 
    } 
 
    //See if the sample rate is supported. 
    if (ioctl(m_nDevID, SOUND_PCM_WRITE_RATE, &ulTmp) == -1) 
    { 
        //Not quite the real error, but it is what we need to return. 
        retCode = RA_AOE_DEVBUSY; 
        goto donechecking; 
    } 
 
    if (ulTmp == 0) 
    { 
	/*  
	 * Some drivers actually set the sample rate on the device, but 
	 * return 0 for the sample rate. On these platforms we just ignore 
	 * the return value and assume the sample rate is set to what was 
	 * requested. 
	 */ 
 
	ulTmp = pFormat->ulSamplesPerSec; 
    } 
 
    //The ESS 1688 Sound card (not the ESS Solo-1) will return sample 
    //rates that are close but not quite the ones we asked for (the 
    //ESS Solo-1 doesn't play mono). I have see freqs like 44194 
    //instead of 44100 (.2%) and 7984 instead of 8000 (.6%). 
    //So, if we are close enough just say it is OK.  
    //How about 1%? 
    fTmp = (float)ulTmp/(float)pFormat->ulSamplesPerSec; 
    if( fabs(1.0-fTmp) > .01 ) 
    { 
        //It is NOT supported 
        retCode = RA_AOE_BADFORMAT; 
        goto donechecking; 
    } 
 
    //Check num channels. 
    if (ioctl(m_nDevID, SOUND_PCM_WRITE_CHANNELS, &nNumChannels) == -1) 
    { 
        retCode = RA_AOE_DEVBUSY; 
        goto donechecking; 
    } 
    else if ( nNumChannels != pFormat->uChannels ) 
    { 
        retCode = RA_AOE_BADFORMAT; 
        goto donechecking; 
    } 
 
    //Check the frame size. 
    if (ioctl(m_nDevID, SNDCTL_DSP_SETFMT, &nBitsPerSample) == -1) 
    { 
       retCode = RA_AOE_DEVBUSY; 
       goto donechecking; 
    } 
    else if ( nBitsPerSample != pFormat->uBitsPerSample ) 
    { 
        retCode = RA_AOE_BADFORMAT; 
        goto donechecking; 
    } 
 
    //Close the audio device. 
  donechecking: 
    _CloseAudio(); 
    m_wLastError = retCode; 
    return retCode; 
} 
 
 
HX_RESULT CAudioOutLinux::_CheckSampleRate( ULONG32 ulSampleRate ) 
{ 
    ULONG32 ulTmp = ulSampleRate; 
 
    m_wLastError = RA_AOE_NOERR; 
     
    //Is the device already open? 
    if( m_nDevID > 0 || RA_AOE_NOERR != _OpenAudio() ) 
    { 
        m_wLastError = RA_AOE_DEVBUSY; 
    } 
    else 
    {    
        //See if the sample rate is supported. 
        if (ioctl(m_nDevID, SOUND_PCM_WRITE_RATE, &ulTmp) == -1) 
        { 
            //Not quite the real error, but it is what we need to return. 
            m_wLastError = RA_AOE_DEVBUSY; 
        } 
        else if( ulSampleRate != ulTmp ) 
        { 
            //It is NOT supported 
            m_wLastError = RA_AOE_BADFORMAT; 
        } 
         
        _CloseAudio(); 
    } 
     
    return m_wLastError; 
} 
 
 
HX_RESULT CAudioOutLinux::_Pause()  
{ 
    m_wLastError = HXR_OK; 
    m_ulPausePosition = m_ulTotalWritten; 
    m_ulTickCount = 0; 
    m_ulLastTimeStamp   = 0; 
    return m_wLastError; 
} 
 
HX_RESULT CAudioOutLinux::_Resume() 
{ 
    m_wLastError = HXR_OK; 
 
    if( m_ulTotalWritten > 0 ) 
    { 
        m_ulTickCount = GetTickCount(); 
        m_ulLastTimeStamp = m_ulTickCount; 
    } 
    return m_wLastError; 
}