📄 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 <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <math.h>
#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<<nPower)<m_wBlockSize && nPower<0xf )
{
nPower++;
}
if( nPower > 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 );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -