📄 audhpux.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 "audhpux.h"
#include "ihxpckts.h"
#include "hxtick.h"
#include "hxprefs.h"
#include "hxstrutl.h"
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <math.h>
#include <sys/audio.h>
#define MAX_DEV_NAME 256
//------------------------------------------
// Ctors and Dtors.
//------------------------------------------
CAudioOutHPUX::CAudioOutHPUX() :
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(FALSE),
m_bGetOSpaceFailed(FALSE),
m_nLastVolume(0),
m_nMinVolume(0),
m_nMaxVolume(0)
{
};
CAudioOutHPUX::~CAudioOutHPUX()
{
//The mixer is opened independently of the audio device. Make sure
//it is celosed.
_CloseAudio();
};
//-------------------------------------------------------
// These Device Specific methods must be implemented
// by the platform specific sub-classes.
//-------------------------------------------------------
INT16 CAudioOutHPUX::_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 CAudioOutHPUX::_SetDeviceConfig( const HXAudioFormat* pFormat )
{
if ( !pFormat )
{
pFormat = &m_lastFormat;
}
else
{
m_lastFormat = *pFormat;
}
m_lastFormat = *pFormat;
if ( m_nDevID < 0 )
return RA_AOE_DEVNOTOPEN;
int nFragSize = 8192;
while (-1 != ioctl(m_nDevID, AUDIO_SET_TXBUFSIZE, nFragSize))
{
nFragSize <<= 1;
}
ioctl(m_nDevID, AUDIO_GET_TXBUFSIZE, &m_ulDeviceBufferSize);
//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 = AUDIO_FORMAT_LINEAR16BIT;
}
else
{
nFormat1 = nFormat2 = AUDIO_FORMAT_LINEAR8BIT;
}
if(ioctl(m_nDevID, AUDIO_SET_DATA_FORMAT, nFormat1) == -1)
{
return ( m_wLastError = RA_AOE_NOTENABLED );
}
m_uSampFrameSize = nSampleWidth/8;
if ( nSampleWidth != pFormat->uBitsPerSample )
{
((HXAudioFormat*)pFormat)->uBitsPerSample = nSampleWidth;
}
//Set number of channels. Stereo or mono.
if (ioctl(m_nDevID, AUDIO_SET_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, AUDIO_SET_SAMPLE_RATE, nSampleRate) == -1)
{
return ( m_wLastError = RA_AOE_NOTENABLED );
}
m_unSampleRate = nSampleRate;
if ( nSampleRate != pFormat->ulSamplesPerSec )
{
((HXAudioFormat*)pFormat)->ulSamplesPerSec = nSampleRate;
}
#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, " Support for old OSS: %d\n", m_bGetOSpaceFailed);
#endif
return RA_AOE_NOERR;
}
void CAudioOutHPUX::_SyncUpTimeStamps(ULONG32 lCount)
{
int bytes2 = 0;
int theErr = -1;
audio_status astatus;
if( !m_bGetODelayFailed )
{
theErr = ::ioctl(m_nDevID, AUDIO_GET_STATUS, &astatus);
}
if( theErr != -1)
{
m_ulLastBytesPlayed = (ULONG32)(m_ulTotalWritten+lCount-astatus.transmit_buffer_count);
m_ulLastTimeStamp = GetTickCount();
}
else
{
//so we don't try it again.
m_bGetODelayFailed = TRUE;
}
}
//Device specific method to write bytes out to the audiodevice and return a
//count of bytes written.
HX_RESULT CAudioOutHPUX::_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 CAudioOutHPUX::_OpenAudio()
{
HX_RESULT retCode = RA_AOE_NOERR;
// Open the audio device if it isn't already open
if ( -1 == m_nDevID )
{
//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/audio", MAX_DEV_NAME );
}
//Set the tick count to zero
m_ulTickCount = 0;
m_ulLastTimeStamp = 0;
m_ulLastBytesPlayed = 0;
m_ulPausePosition = 0;
m_nDevID = ::open( szDevName, O_WRONLY );
}
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.
retCode = RA_AOE_BADOPEN;
}
m_wLastError = retCode;
return m_wLastError;
}
HX_RESULT CAudioOutHPUX::_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 CAudioOutHPUX::_OpenMixer()
{
if (-1 == m_nDevID)
_OpenAudio(); // control the level through the audio device
// get and store the current volume
struct audio_describe adescribe;
if (-1 != ioctl(m_nDevID, AUDIO_DESCRIBE, &adescribe)) {
m_nMinVolume = adescribe.min_transmit_gain;
m_nMaxVolume = adescribe.max_transmit_gain;
}
struct audio_gain gainsettings;
if (-1 != ioctl(m_nDevID, AUDIO_GET_GAINS, &gainsettings))
{
m_nLastVolume = (gainsettings.cgain[0].transmit_gain -
m_nMinVolume) * 100 /
(m_nMaxVolume - m_nMinVolume);
}
m_bMixerPresent=TRUE;
return RA_AOE_NOERR;
}
HX_RESULT CAudioOutHPUX::_CloseMixer()
{
HX_RESULT retCode = RA_AOE_NOERR;
m_wLastError = retCode;
m_bMixerPresent = FALSE;
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 CAudioOutHPUX::_Reset()
{
ioctl(m_nDevID, AUDIO_RESET, RESET_TX_BUF);
_CloseAudio();
UINT32 ulTickCount = m_ulTickCount;
UINT32 ulLastTimeStamp = m_ulLastTimeStamp;
UINT32 ulLastBytesPlayed = m_ulLastBytesPlayed;
UINT32 ulPausePosition = m_ulPausePosition;
_OpenAudio();
m_ulTickCount = ulTickCount;
m_ulLastTimeStamp = ulLastTimeStamp;
m_ulLastBytesPlayed = ulLastBytesPlayed;
m_ulPausePosition = ulPausePosition;
_SetDeviceConfig(NULL);
m_ulPausePosition = 0;
HX_RESULT retCode = RA_AOE_NOERR;
m_wLastError = retCode;
// verify that this worked
audio_status astatus;
ioctl(m_nDevID, AUDIO_GET_STATUS, &astatus);
return m_wLastError;
}
//Device specific method to get/set the devices current volume.
UINT16 CAudioOutHPUX::_GetVolume() const
{
_OpenMixer();
return m_nLastVolume;
}
HX_RESULT CAudioOutHPUX::_SetVolume(UINT16 unVolume)
{
m_nLastVolume = unVolume;
HX_RESULT retCode = RA_AOE_NOERR;
struct audio_gain gainsettings;
memset(&gainsettings, 0, sizeof(audio_gain));
gainsettings.channel_mask = AUDIO_CHANNEL_LEFT | AUDIO_CHANNEL_RIGHT;
gainsettings.cgain[0].transmit_gain = (INT32)unVolume * (m_nMaxVolume - m_nMinVolume) / 100 + m_nMinVolume;
gainsettings.cgain[1].transmit_gain = (INT32)unVolume * (m_nMaxVolume - m_nMinVolume) / 100 + m_nMinVolume;
if (::ioctl( m_nDevID, AUDIO_SET_GAINS, &gainsettings) < 0)
{
retCode = RA_AOE_NOTENABLED;
}
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 CAudioOutHPUX::_Drain()
{
HX_RESULT retCode = RA_AOE_NOERR;
m_wLastError = retCode;
return m_wLastError;
}
ULONG32 CAudioOutHPUX::_GetBytesActualyPlayed(void) const
{
/* Get current playback position in device DMA. */
int bytes2 = 0;
ULONG32 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 )
{
((CAudioOutHPUX*)this)->_SyncUpTimeStamps(); // ug... constness of method prohibits this call somtimes
ulTick = GetTickCount();
}
ulTheAnswer = (ULONG32)(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 = (ULONG32)((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 CAudioOutHPUX::_GetRoomOnDevice(ULONG32& ulBytes) const
{
audio_status astatus;
int theErr;
theErr = ioctl(m_nDevID, AUDIO_GET_STATUS, &astatus);
if( theErr != -1)
{
ulBytes = m_ulDeviceBufferSize - astatus.transmit_buffer_count;
}
return RA_AOE_NOERR;
}
HX_RESULT CAudioOutHPUX::_CheckFormat( const HXAudioFormat* pFormat )
{
return RA_AOE_NOERR;
}
HX_RESULT CAudioOutHPUX::_CheckSampleRate( ULONG32 ulSampleRate )
{
return m_wLastError = RA_AOE_NOERR;
}
HX_RESULT CAudioOutHPUX::_Pause()
{
// ioctl(m_nDevID, AUDIO_PAUSE, AUDIO_TRANSMIT);
m_wLastError = HXR_OK;
m_ulPausePosition = m_ulTotalWritten;
m_ulTickCount = 0;
m_ulLastTimeStamp = 0;
return m_wLastError;
}
HX_RESULT CAudioOutHPUX::_Resume()
{
// ioctl(m_nDevID, AUDIO_RESUME, AUDIO_TRANSMIT);
m_wLastError = HXR_OK;
if( m_ulTotalWritten > 0 )
{
m_ulTickCount = GetTickCount();
m_ulLastTimeStamp = m_ulTickCount;
}
return m_wLastError;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -