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; }