📄 audaix.cpp
字号:
/* ***** BEGIN LICENSE BLOCK ***** * Source last modified: $Id: audaix.cpp,v 1.2.42.1 2004/07/09 02:01:42 hubbe Exp $ * * Portions Copyright (c) 1995-2004 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 (the "RPSL") available at * http://www.helixcommunity.org/content/rpsl unless you have licensed * the file under the current version of the RealNetworks Community * Source License (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. * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL") in which case the provisions of the GPL are applicable * instead of those above. If you wish to allow use of your version of * this file only under the terms of the GPL, and not to allow others * to use your version of this file under the terms of either the RPSL * or RCSL, indicate your decision by deleting the provisions above * and replace them with the notice and other provisions required by * the GPL. If you do not delete the provisions above, a recipient may * use your version of this file under the terms of any one of the * RPSL, the RCSL or the GPL. * * 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 rightstatic const LONG32 lDefaultBalance = 0; static const LONG32 lDefaultVolume = 50; // UMS range is 0..100static 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 subsystemstatic 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 ); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -