📄 audaix.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 ***** */
/*******************************************************************
* audaix.cpp
*
* CLASS: CAudioOutAIX
*
* DESCRIPTION: AIX & UMS specific audio class implementation
*
*******************************************************************/
#include <signal.h> // for getenv()
#include <stdio.h>
#include <stdlib.h> // for getenv()
#include <math.h>
#include <sys/types.h>
#include <stropts.h> // for I_FLUSH ioctl
#include <sys/conf.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/audio.h>
#include "hxcom.h"
#include "hxresult.h"
#include "hxengin.h"
#include "ihxpckts.h" // for IHXBuffer
#include "hxslist.h"
#include "timeval.h"
#include "audaix.h"
#include "hxaudses.h"
#include "hxtick.h"
#include "chxpckts.h"
#include "debug.h"
#include "hxstrutl.h"
#include <UMSBAUDDevice.xh>
#include <UMSAudioDevice.xh>
#include <UMSAudioTypes.xh>
struct IHXCallback; // forward declaration needed for callback.
// One and ONLY one of the following must be uncommented. They determine the
// strategy used to determine the elaped time. The first is the preferred.
#define AIX_TIME_BYTES_WRITTEN // function of bytes written vrs bytes in buffer.
//**********************************************************************
// constants.
//
// in UMS, balance is really an initial pan setting.
// -100 = hard left, 0 is centered, 100 = hard right
static const LONG32 lDefaultBalance = 0;
static const LONG32 lDefaultVolume = 50; // UMS range is 0..100
static LONG32 zlCurrentVolume = 50; // UMS range is 0..100
// this is sufficient for PCI devices, for microchannel devices, set the
// envar AUDIODEV to "/dev/maud0".
static const char * szDefaultPortFilename = "/dev/paud0";
static const char * moduleName = "CAudioOutAIX";
// this will point to the SOM environment needed by the UMS subsystem
static Environment* gpSomEnvironment = NULL;
// static local routines, used to translate UMS message code to RMA types.
static const char * getUMSAudioDeviceError( UMSAudioDevice_ReturnCode );
static audio_error UMSErrorCodeToRACode( UMSAudioDevice_ReturnCode );
CAudioOutAIX::CAudioOutAIX()
: mixm_wID( -1 ),
m_wState( RA_AOS_CLOSED ),
m_wLastError( RA_AOE_NOERR ),
m_bMixerPresent(FALSE),
m_wBlockSize(0),
m_ulLastNumBytes (0),
m_ulBytesRemaining(0),
m_ulTotalWritten(0),
m_bFirstWrite (TRUE),
m_pPlaybackCountCBTime(NULL),
m_PendingCallbackID (0),
m_bCallbackPending(FALSE),
m_paused(FALSE),
m_pWriteList(NULL),
m_lLeftGain(100),
m_lRightGain(100)
{
// set up UMS environment.
gpSomEnvironment = somGetGlobalEnvironment();
HX_ASSERT( gpSomEnvironment );
m_pAudioDevice = new UMSBAUDDevice();
// Get AUDIODEV environment var to find audio device of choice
char *adev = (char*)getenv("AUDIODEV"); /* Flawfinder: ignore */
char *mdev = (char*)getenv("MIXERDEV"); /* Flawfinder: ignore */
// Use defaults if no environment variable is set.
m_DevName[26] = NULL;
m_DevCtlName[26] = NULL;
if (adev && strlen(adev) > 0)
SafeStrCpy(m_DevName, adev, 26);
else
SafeStrCpy(m_DevName, szDefaultPortFilename, 26);
m_pPlaybackCountCBTime = new Timeval;
if (mdev && strlen(mdev) > 0)
SafeStrCpy(m_DevCtlName, mdev, 26);
else
SafeStrCpy(m_DevCtlName, szDefaultPortFilename, 26);
m_pWriteList = new CHXSimpleList();
// now configure our device.
m_pAudioDevice->set_audio_format_type ( gpSomEnvironment, "PCM" );
m_pAudioDevice->set_number_format ( gpSomEnvironment,
"TWOS_COMPLEMENT" );
m_pAudioDevice->set_byte_order ( gpSomEnvironment, "MSB" );
m_pAudioDevice->set_time_format ( gpSomEnvironment,
UMSAudioTypes_Msecs );
m_pAudioDevice->set_balance ( gpSomEnvironment, lDefaultBalance );
m_pAudioDevice->set_volume ( gpSomEnvironment, zlCurrentVolume );
m_pAudioDevice->enable_output ( gpSomEnvironment, "LINE_OUT",
&m_lLeftGain, &m_lRightGain );
}
CAudioOutAIX::~CAudioOutAIX()
{
if ( m_wState != RA_AOS_CLOSED )
{
_Imp_Close();
m_wState = RA_AOS_CLOSED;
}
mixm_wID = -1;
m_wState = RA_AOS_CLOSED;
m_wLastError = RA_AOE_NOERR;
m_bMixerPresent = FALSE;
m_wBlockSize = 0;
m_ulLastNumBytes = 0;
m_ulBytesRemaining = 0;
m_ulTotalWritten = 0;
if (m_pScheduler)
m_pScheduler->Release();
m_PendingCallbackID = 0;
m_bCallbackPending = TRUE;
while(!m_pWriteList->IsEmpty())
{
IHXBuffer* pBuffer = (IHXBuffer*)m_pWriteList->RemoveHead();
HX_RELEASE(pBuffer);
}
HX_DELETE(m_pWriteList);
delete m_pAudioDevice;
}
/*
* I do not open /dev/mixer to control volume, the volume is scaled
* by the UMS device.
* The device's volume has a range of 0..100.
*/
UINT16 CAudioOutAIX::_Imp_GetVolume()
{
long volume;
m_pAudioDevice->get_volume( gpSomEnvironment, &volume );
return zlCurrentVolume;
return (UINT16)volume;
}
HX_RESULT CAudioOutAIX::_Imp_SetVolume( const UINT16 uVolume )
{
m_pAudioDevice->set_volume( gpSomEnvironment, (long)uVolume );
zlCurrentVolume = uVolume;
return RA_AOE_NOERR;
}
/*
* All UMS audio devices support volume.
*/
BOOL CAudioOutAIX::_Imp_SupportsVolume()
{
return TRUE;
}
HX_RESULT CAudioOutAIX:: _Imp_Open( const HXAudioFormat* pFormatSupplied )
{
if( pFormatSupplied != NULL )
m_pAudioFormat = pFormatSupplied;
HX_ASSERT( m_pAudioFormat != NULL );
// Get the core scheduler interface; Use this to schedule polling
// the audio device for number of bytes played.
if ( m_pContext )
{
m_pContext->QueryInterface(IID_IHXScheduler, (void**) &m_pScheduler );
}
// Check state. Could already be opened.
if ( m_wState == RA_AOS_OPEN_PAUSED ||
m_wState == RA_AOS_OPEN_PLAYING ||
m_wState == RA_AOS_OPENING )
return RA_AOE_NOERR;
m_ulBlocksProcessed = 0;
UMSAudioDevice_ReturnCode retCode =
m_pAudioDevice->open( gpSomEnvironment, m_DevName, "PLAY", 0 );
if( retCode != UMSAudioDevice_Success )
{
return HXR_FAILED;
}
m_wBlockSize = m_ulBytesPerGran; //m_pAudioFormat->uMaxBlockSize;
m_uSampFrameSize = m_pAudioFormat->uBitsPerSample / 8;
// Set device state
m_wState = RA_AOS_OPENING;
// Configure the audio device.
AUDIOERROR iVal = SetDeviceConfig( m_pAudioFormat );
return RA_AOE_NOERR;
}
HX_RESULT CAudioOutAIX::_Imp_Close()
{
m_wState = RA_AOS_CLOSED;
// this will force the player to play all remaining data. If passed TRUE,
// the call will block until finished playing. Do we want to call this?
// m_pAudioDevice->play_remaining_data( gpSomEnvironment, TRUE );
_Imp_Write(NULL);
m_pAudioDevice->stop( gpSomEnvironment );
m_pAudioDevice->close( gpSomEnvironment );
// Remove callback from scheduler
if (m_bCallbackPending)
{
m_pScheduler->Remove(m_PendingCallbackID);
m_bCallbackPending = FALSE;
}
return RA_AOE_NOERR;
}
HX_RESULT CAudioOutAIX::_Imp_Write( const HXAudioData* pAudioOutHdr )
{
IHXBuffer* pBuffer = NULL;
UCHAR* pData = NULL;
UMSAudioTypes_Buffer adBuffer;
UINT32* pTimeStamp = NULL;
if ( m_bFirstWrite && pAudioOutHdr)
{
/* Initialize the playback callback time. */
HXTimeval lTime = m_pScheduler->GetCurrentSchedulerTime();
m_pPlaybackCountCBTime->tv_sec = lTime.tv_sec;
m_pPlaybackCountCBTime->tv_usec = lTime.tv_usec;
m_bFirstWrite = FALSE;
ReschedPlaybackCheck();
}
if (pAudioOutHdr)
{
++m_ulBlocksProcessed ;
DPRINTF(D_INFO, ("_Imp_Write: buf len in milli-seconds: %lu\n",
((ULONG32) (( 1000.0
/ (m_num_channels * m_uSampFrameSize * m_sample_rate))
* pAudioOutHdr->pData->GetSize()))));
}
// If we are paused, just add the block to the end of the WriteList.
if (m_paused)
{
if (!pAudioOutHdr)
return m_wLastError = RA_AOE_NOERR;
IHXBuffer* pNewBuffer = new CHXBuffer();
pNewBuffer->Set(pAudioOutHdr->pData->GetBuffer(),
pAudioOutHdr->pData->GetSize());
pNewBuffer->AddRef();
m_pWriteList->AddTail(pNewBuffer);
return RA_AOE_NOERR;
}
BOOL bWroteSomething;
do {
bWroteSomething = FALSE;
if(m_pWriteList->GetCount() <= 0)
{
if(!pAudioOutHdr)
return RA_AOE_NOERR;
pData = (UCHAR*)pAudioOutHdr->pData->GetBuffer();
adBuffer._length = pAudioOutHdr->pData->GetSize();
}
else
{
if (pAudioOutHdr)
{
IHXBuffer* pNewBuffer = new CHXBuffer();
pNewBuffer->AddRef();
pNewBuffer->Set(pAudioOutHdr->pData->GetBuffer(),
pAudioOutHdr->pData->GetSize());
m_pWriteList->AddTail(pNewBuffer);
}
pBuffer = (IHXBuffer*)m_pWriteList->RemoveHead();
HX_ASSERT(pBuffer);
pData = pBuffer->GetBuffer();
adBuffer._length = pBuffer->GetSize();
}
// Write audio data to device.
long count = 0;
int wrote;
adBuffer._buffer = pData;
adBuffer._maximum = adBuffer._length;
int nBytesPerSample = (m_bits_per_sample/8)* m_num_channels;
long lSamplesToWrite = adBuffer._length / nBytesPerSample;
long lSamplesWritten =0;
int transferredCount = adBuffer._length;
m_wLastError = RA_AOE_NOERR; // less work this way.
UMSAudioDevice_ReturnCode retCode =
m_pAudioDevice->write( gpSomEnvironment,
&adBuffer,
lSamplesToWrite,
&lSamplesWritten );
switch(retCode)
{
case UMSAudioDevice_Success :
{
int nBytesWritten = lSamplesWritten * nBytesPerSample;
m_ulTotalWritten += nBytesWritten;
HX_ASSERT(lSamplesWritten <= lSamplesToWrite);
if( lSamplesWritten == lSamplesToWrite )
{
bWroteSomething = TRUE;
}
else
{
/* requeue the balance. */
long lSamplesToRequeue = lSamplesToWrite - lSamplesWritten;
IHXBuffer* pNewBuffer = new CHXBuffer();
pNewBuffer->AddRef();
pNewBuffer->Set( adBuffer._buffer + nBytesWritten,
adBuffer._length - nBytesWritten );
m_pWriteList->AddHead(pNewBuffer);
}
}
break;
case UMSAudioDevice_DeviceError : // indicates EWOULDBLOCK
case UMSAudioDevice_Preempted :
case UMSAudioDevice_Interrupted :
{
// just requeue the data
IHXBuffer* pNewBuffer = new CHXBuffer();
pNewBuffer->AddRef();
pNewBuffer->Set( adBuffer._buffer, adBuffer._length );
m_pWriteList->AddHead(pNewBuffer);
break;
}
default : // failure!
HX_ASSERT(FALSE);
m_wLastError = UMSErrorCodeToRACode( retCode );
}
INT32 newRefCount = 0;
if( pBuffer )
newRefCount = pBuffer->Release();
HX_ASSERT(newRefCount == 0);
pBuffer = NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -