www.pudn.com > helix.src.0812.rar > audlinux_esound.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 ***** */  
 
//=================================================================================== 
//  
//   	audESound.cpp 
//  
//   	CLASS: CAudioOutESound 
// 	 
//   	Implements the sound subsystem for the Enlightenment Sound 
//      Deamon. 
// 
//=================================================================================== 
 
#include  
#include  
#include  
#include  
#include  
#include   
#include  
 
 
#include "audlinux_esound.h" 
#include "ihxpckts.h" 
#include "hxtick.h" 
 
//------------------------------------------ 
// Ctors and Dtors. 
//------------------------------------------ 
CAudioOutESound::CAudioOutESound() : 
    CAudioOutUNIX(), 
    m_nDevID(NO_FILE_DESCRIPTOR), 
    m_nESoundServerID(NO_FILE_DESCRIPTOR), 
    m_nESoundPlayerID(NO_FILE_DESCRIPTOR), 
    m_ulTickCount(0), 
    m_ulPausePosition(0), 
    m_strRealplayerName(""), 
    m_pESDLib(NULL), 
    m_fpESDPlayStream(NULL), 
    m_fpESDGetAllInfo(NULL), 
    m_fpESDFreeAllInfo(NULL), 
    m_fpESDClose(NULL), 
    m_fpESDSetStreamPan(NULL), 
    m_fpESDAudioFlush(NULL), 
    m_fpESDOpenSound(NULL) 
{ 
    //Construct a proccess specific name to register with ESD. 
    m_strRealplayerName.Format( "%s-%d", "realplayer", getpid() ); 
}; 
 
CAudioOutESound::~CAudioOutESound() 
{ 
    HX_DELETE( m_pESDLib ); 
}; 
 
//------------------------------------------------------- 
// These Device Specific methods must be implemented  
// by the platform specific sub-classes. 
//------------------------------------------------------- 
INT16 CAudioOutESound::_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 CAudioOutESound::_SetDeviceConfig( const HXAudioFormat* pFormat ) 
{ 
    if ( m_nESoundServerID < 0 ) 
        return RA_AOE_DEVNOTOPEN; 
 
    //We can only do this once after opening the audio device. 
    if( m_nDevID != NO_FILE_DESCRIPTOR ) 
        return RA_AOE_DEVBUSY; 
     
    // Open the audio device if it isn't already open 
    esd_format_t format = ESD_STREAM | ESD_PLAY; 
 
    //Set steareo or mono 
    if( 2 == pFormat->uChannels ) 
    { 
        format |= ESD_STEREO; 
    } 
    else 
    { 
        format |= ESD_MONO; 
    } 
     
    //Now set the format. Either 8-bit or 16-bit audio is supported. 
    if( pFormat->uBitsPerSample == 16) 
    { 
        format |= ESD_BITS16; 
    } 
    else 
    { 
        format |= ESD_BITS8; 
    } 
 
    //Now open our connection with ESD on the local host. 
    m_nDevID = m_fpESDPlayStream( format, pFormat->ulSamplesPerSec, NULL, (const char *)m_strRealplayerName); 
     
    if ( m_nDevID < 0 ) 
    { 
#ifdef _DEBUG         
        fprintf( stderr, "Failed to open audio!!!!!!! Code is: %d  errno: %d\n", 
                 m_nDevID, errno ); 
#endif         
         
        //Error opening device. 
        return RA_AOE_DEVNOTOPEN; 
    } 
 
    m_wBlockSize         = m_ulBytesPerGran; 
    m_unNumChannels      = pFormat->uChannels; 
    m_unSampleRate       = pFormat->ulSamplesPerSec; 
    m_ulDeviceBufferSize = ESD_BUF_SIZE*4; 
    m_uSampFrameSize     = pFormat->uBitsPerSample/8; 
 
    //Now, here is the tricky part. We must get a list of players from 
    //the esd deamon and interate through them until we find our self. 
    //Was we find us we need to store the ID for later use in setting 
    //the volume (panning). 
    //n 
    // From esd.h: 
    // 
    //      typedef struct esd_info { 
    //         
    //          esd_server_info_t *server; 
    //          esd_player_info_t *player_list; 
    //          esd_sample_info_t *sample_list; 
    // 
    //      } esd_info_t; 
    // 
    //  typedef struct esd_player_info { 
    //      struct esd_player_info *next; /* point to next entry in list */ 
    //      esd_server_info_t *server;	/* the server that contains this stream */ 
    //      int source_id;		/* either a stream fd or sample id */ 
    //      char name[ ESD_NAME_MAX ];	/* name of stream for remote control */ 
    //      int rate;			/* sample rate */ 
    //      int left_vol_scale;		/* volume scaling */ 
    //      int right_vol_scale; 
    //      esd_format_t format;	/* magic int with the format info */ 
    //  } esd_player_info_t; 
 
    esd_player_info_t *pPlayerInfo = _GetPlayerInfo(); 
    if( NULL == pPlayerInfo ) 
    { 
        return RA_AOE_GENERAL; 
    } 
 
    m_nESoundPlayerID = pPlayerInfo->source_id; 
    HX_DELETE( pPlayerInfo ); 
 
    if( m_nESoundPlayerID == NO_FILE_DESCRIPTOR ) 
    { 
#ifdef _DEBUG         
        fprintf( stderr, "Can't find the realaudio stream in the ESD server list.\n"); 
#endif 
        return RA_AOE_GENERAL; 
    } 
 
#ifdef _DEBUG 
    fprintf( stderr, "Device Configured:\n"); 
    fprintf( stderr, "        Sample Rate: %d\n", m_unSampleRate); 
    fprintf( stderr, "       Sample Width: %d\n", m_uSampFrameSize); 
    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); 
#endif 
 
    return RA_AOE_NOERR; 
} 
 
//FREE THE RETURNED POINTER!!!!!!!!!!!!!!! 
esd_player_info_t* CAudioOutESound::_GetPlayerInfo() const 
{ 
     
    esd_info_t       *pServerInfo  = NULL; 
    esd_player_info_t *pPlayerInfo = NULL; 
    esd_player_info_t *pRetVal     = NULL; 
 
    pServerInfo = m_fpESDGetAllInfo(m_nESoundServerID); 
    if( pServerInfo == NULL ) 
    { 
#ifdef _DEBUG         
        fprintf( stderr, "Can't get server info from ESD.\n"); 
#endif 
        return NULL; 
    } 
    if( pServerInfo->player_list == NULL ) 
    { 
#ifdef _DEBUG         
        fprintf( stderr, "There seem to be no players connected to esd server.\n"); 
#endif 
        m_fpESDFreeAllInfo( pServerInfo ); 
        return NULL; 
    } 
    pPlayerInfo = pServerInfo->player_list; 
    while( pPlayerInfo ) 
    { 
        if( strcmp( pPlayerInfo->name, (const char *)m_strRealplayerName ) == 0 ) 
        { 
            //found it. 
            break; 
        } 
        pPlayerInfo = pPlayerInfo->next; 
    } 
 
    //We found it. Make a new one and copy. 
    pRetVal = new esd_player_info_t(*pPlayerInfo); 
 
    if( NULL == pRetVal ) 
    { 
        //OOps. 
        return NULL; 
    } 
     
    pRetVal->next    = NULL; //Don't even think about it. 
    pRetVal->server  = NULL; 
//      pRetVal->source_id = pPlayerInfo->source_id; 
//      strcpy(pRetVal->name, pPlayerInfo->name ); 
//      pRetVal->rate = pPlayerInfo->rate; 
//      pRetVal->left_vol_scale = pPlayerInfo->left_vol_scale; 
//      pRetVal->right_vol_scale = pPlayerInfo->right_vol_scale 
//      pRetVal->format = pPlayerInfo->format; 
     
    //Free the info struct. 
    m_fpESDFreeAllInfo( pServerInfo ); 
 
    return pRetVal; 
} 
 
//Device specific method to write bytes out to the audiodevice and return a 
//count of bytes written.  
HX_RESULT CAudioOutESound::_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; 
        } 
         
    } 
    return retCode; 
} 
 
//XXXgfw, since we have to open the audio device in ESD with 
//all the format information (no way to change it once open) 
//we will just return OK here and do the actual open in  
//SetDeviceConfig() call. In this call we will just open 
//our connectin to the ESD server that we use for changing 
//the volume of our stream (panning). 
HX_RESULT CAudioOutESound::_OpenAudio() 
{ 
     
    HX_RESULT retCode = RA_AOE_NOERR; 
     
    //Set the tick count to zero 
    m_ulTickCount = 0; 
    m_ulPausePosition = 0; 
 
    // create DLLAccess object 
    m_pESDLib = new DLLAccess(); 
 
    if((DLLAccess::DLL_OK==m_pESDLib->open("libesd.so", DLLTYPE_NOT_DEFINED))|| 
       (DLLAccess::DLL_OK==m_pESDLib->open("libesd.so.0", DLLTYPE_NOT_DEFINED))) 
    { 
        m_fpESDPlayStream = (ESDPlayStreamType)m_pESDLib->getSymbol("esd_play_stream"); 
        if( dlerror() != NULL ) 
            retCode = RA_AOE_DEVNOTOPEN; 
        m_fpESDGetAllInfo = (ESDGetAllInfoType)m_pESDLib->getSymbol("esd_get_all_info"); 
        if( dlerror() != NULL ) 
            retCode = RA_AOE_DEVNOTOPEN; 
        m_fpESDFreeAllInfo = (ESDFreeAllInfoType)m_pESDLib->getSymbol("esd_free_all_info"); 
        if( dlerror() != NULL ) 
            retCode = RA_AOE_DEVNOTOPEN; 
        m_fpESDClose = (ESDCloseType)m_pESDLib->getSymbol("esd_close"); 
        if( dlerror() != NULL ) 
            retCode = RA_AOE_DEVNOTOPEN; 
        m_fpESDSetStreamPan = (ESDSetStreamPanType)m_pESDLib->getSymbol("esd_set_stream_pan"); 
        if( dlerror() != NULL ) 
            retCode = RA_AOE_DEVNOTOPEN; 
        m_fpESDAudioFlush = (ESDAudioFlushType)m_pESDLib->getSymbol("esd_audio_flush"); 
        if( dlerror() != NULL ) 
            retCode = RA_AOE_DEVNOTOPEN; 
        m_fpESDOpenSound = (ESDOpenSoundType)m_pESDLib->getSymbol("esd_open_sound"); 
        if( dlerror() != NULL ) 
            retCode = RA_AOE_DEVNOTOPEN; 
    } 
    else 
    { 
#ifdef _DEBUG         
        //Can't load the ESD shared lib. Tell the user. 
        fprintf( stderr, "The ESD library, libesd.so, could not be loaded.\n"); 
        fprintf( stderr, "Please install ESD, disable ESD support or locate\n" ); 
        fprintf( stderr, "the missing library.\n"); 
#endif 
        retCode = RA_AOE_DEVNOTOPEN; 
    } 
     
    //Open the ESD server on the localhost. 
    if( RA_AOE_NOERR == retCode ) 
    { 
        m_nESoundServerID = m_fpESDOpenSound( NULL ); 
        if( m_nESoundServerID == -1 ) 
        { 
#ifdef _DEBUG         
            fprintf( stderr, "The ESD server could not be located on localhost.\n"); 
            fprintf( stderr, "Either disable ESD support or start the esd daemon.\n"); 
#endif         
            retCode = RA_AOE_DEVNOTOPEN; 
        } 
    } 
     
    m_wLastError = retCode; 
    return m_wLastError; 
} 
 
 
HX_RESULT CAudioOutESound::_CloseAudio() 
{ 
    HX_RESULT retCode = RA_AOE_NOERR; 
     
    if( m_nDevID >= 0 ) 
    { 
        //close the esd server 
        m_fpESDClose( m_nESoundServerID ); 
        m_nESoundServerID = NO_FILE_DESCRIPTOR; 
 
        //Close the esd player FD. 
        ::close( m_nDevID ); 
        m_nDevID = NO_FILE_DESCRIPTOR; 
    } 
    else 
    { 
        retCode = RA_AOE_DEVNOTOPEN; 
    } 
 
    m_ulPausePosition = 0; 
    m_ulTickCount     = 0; 
     
    m_wLastError = retCode; 
    return m_wLastError; 
} 
 
HX_RESULT CAudioOutESound::_OpenMixer() 
{ 
    HX_RESULT retCode = RA_AOE_NOERR; 
 
    //ESD always has volume support. 
    m_bMixerPresent = 1; 
    _Imp_GetVolume(); 
     
    m_wLastError = retCode; 
    return m_wLastError; 
} 
 
//Do nothing under ESD. 
HX_RESULT CAudioOutESound::_CloseMixer() 
{ 
    HX_RESULT retCode = RA_AOE_NOERR; 
     
    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. 
//Can't really do anything here under ESD. 
HX_RESULT CAudioOutESound::_Reset() 
{ 
    HX_RESULT retCode = RA_AOE_NOERR; 
 
    if ( m_nDevID < 0 ) 
    { 
        retCode = RA_AOE_DEVNOTOPEN; 
    } 
 
    m_ulPausePosition = 0; 
     
    m_wLastError = retCode; 
    return m_wLastError; 
} 
 
//Device specific method to get/set the devices current volume. 
UINT16 CAudioOutESound::_GetVolume() const 
{ 
    int nRetVolume   = 0; 
 
    if( m_nESoundPlayerID > 0 ) 
    { 
        //We have been added to the esd server list and can report 
        //volume. 
        esd_player_info_t *pPlayer = _GetPlayerInfo(); 
        if( NULL != pPlayer ) 
        { 
            //Choose either the left or right? 
            nRetVolume = pPlayer->left_vol_scale; 
            HX_DELETE( pPlayer ); 
        } 
    } 
    else 
    { 
        //ESD always starts out an app at 256. ESD_VOLUME_BASE. 
        nRetVolume = ESD_VOLUME_BASE; 
    } 
 
    //Map device specific volume levels to [0,100].  
    nRetVolume = (int) ((float)nRetVolume/256.0*100.0+0.5); 
 
    return nRetVolume;  
} 
 
HX_RESULT CAudioOutESound::_SetVolume(UINT16 unVolume) 
{ 
    HX_RESULT retCode = RA_AOE_NOERR; 
 
    //Map incoming [0..100] volume to ESD_VOLUME_BASE. 
    unVolume = (int)((float)unVolume/100.0 * (float)ESD_VOLUME_BASE + 0.5 ); 
 
    if( m_nESoundPlayerID > 0 ) 
    { 
        //We have been added to the esd server list and can set 
        //volumes. 
        m_fpESDSetStreamPan( m_nESoundServerID, m_nESoundPlayerID, unVolume, unVolume); 
    } 
    else 
    { 
        //Just do nothing here for now.... 
    } 
 
    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 CAudioOutESound::_Drain() 
{ 
    HX_RESULT retCode = RA_AOE_NOERR; 
     
    if ( m_nDevID < 0 ) 
    { 
        retCode = RA_AOE_DEVNOTOPEN; 
    } 
 
    m_fpESDAudioFlush(); 
     
    m_wLastError = retCode; 
    return m_wLastError; 
} 
 
 
 
UINT64 CAudioOutESound::_GetBytesActualyPlayed(void) const 
{ 
    UINT64 ulBytes2 = 0; 
 
    if( m_ulTotalWritten > 0 ) 
    { 
        ulBytes2 = ((double)((GetTickCount()-m_ulTickCount)*m_unNumChannels)/(double)1000*m_unSampleRate*m_uSampFrameSize); 
        ulBytes2 += m_ulPausePosition; 
    } 
 
    return  ulBytes2; 
} 
 
 
//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 CAudioOutESound::_GetRoomOnDevice(ULONG32& ulBytes) const 
{ 
    HX_RESULT retCode     = RA_AOE_NOERR; 
 
    //XXXgfw :-( This is going to suck if they don't use threads... 
    ulBytes = m_wBlockSize; 
//    ulBytes = m_ulDeviceBufferSize-(m_ulTotalWritten-_GetBytesActualyPlayed() ); 
 
    m_wLastError = retCode; 
    return m_wLastError; 
} 
 
HX_RESULT CAudioOutESound::_CheckFormat( const HXAudioFormat* pFormat ) 
{ 
    m_wLastError = RA_AOE_NOERR; 
    return m_wLastError; 
} 
 
 
HX_RESULT CAudioOutESound::_CheckSampleRate( ULONG32 ulSampleRate ) 
{ 
    //ESD supposidly converts any format to one the matches the  
    //installed hardware the best. ESD does the conversion for 
    //us. So, Just return OK. 
    m_wLastError = RA_AOE_NOERR; 
    return m_wLastError; 
} 
  
 
HX_RESULT CAudioOutESound::_Pause()  
{ 
    m_wLastError = HXR_OK; 
    m_ulPausePosition = m_ulTotalWritten; 
    m_ulTickCount = 0; 
    return m_wLastError; 
} 
 
HX_RESULT CAudioOutESound::_Resume() 
{ 
    m_wLastError = HXR_OK; 
 
    if( m_ulTotalWritten > 0 ) 
        m_ulTickCount = GetTickCount(); 
     
    return m_wLastError; 
} 
 
BOOL CAudioOutESound::_IsSelectable() const 
{ 
    return TRUE; 
} 
 
 
BOOL CAudioOutESound::_HardwarePauseSupported() const 
{ 
    return TRUE; 
}