audUnix.cpp
上传用户:zhongxx05
上传日期:2007-06-06
资源大小:33641k
文件大小:31k
- /* ***** 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 "hxcom.h"
- #include "hxresult.h"
- #include "hxengin.h"
- #include "ihxpckts.h" // for IHXBuffer
- #include "hxslist.h"
- #include "timeval.h"
- #include "hxausvc.h"
- #include "auderrs.h"
- #include "hxaudev.h"
- #include "hxaudses.h"
- #include "hxtick.h"
- #include "chxpckts.h"
- #include "debug.h"
- #include "microsleep.h"
- //me.
- #include "audUnix.h"
- #include <errno.h>
- #if defined(_THREADED_AUDIO) && defined(_UNIX_THREADS_SUPPORTED)
- #include "hxprefs.h"
- #endif
- //-1 is usually considered to be no file descriptor.
- const int CAudioOutUNIX::NO_FILE_DESCRIPTOR = -1;
- const int CAudioOutUNIX::MAX_VOLUME = 100;
-
- //XXXgfw We need to clean up the return values. We need to return only PN result codes
- //XXXgfw and not RA_AOE codes from interface methods.
- CAudioOutUNIX::CAudioOutUNIX() :
- m_pCallback(NULL),
- m_wState( RA_AOS_CLOSED ),
- m_wLastError( RA_AOE_NOERR ),
- m_bMixerPresent(FALSE),
- m_wBlockSize(0),
- m_ulLastNumBytes (0),
- m_ulTotalWritten(0),
- m_bFirstWrite (TRUE),
- m_pPlaybackCountCBTime(0),
- m_PendingCallbackID (0),
- m_bCallbackPending(FALSE),
- m_pWriteList(NULL),
- m_ulDeviceBufferSize(0),
- #if defined(_THREADED_AUDIO) && defined(_UNIX_THREADS_SUPPORTED)
- m_mtxWriteListPlayStateLock(NULL),
- m_mtxDeviceStateLock(NULL),
- m_audioThread(NULL),
- m_bUserWantsThreads(TRUE),
- m_ulSleepTime(0),
- #endif
- m_pRollbackBuffer(NULL)
- {
- //Alloc a Timeval for use with the timer callback.
- m_pPlaybackCountCBTime = new Timeval;
- m_pCallback = new HXPlaybackCountCB(this);
- m_pCallback->AddRef();
-
- //Allco our write buffer list. Want to throw from here? You will, like
- //it or not.
- m_pWriteList = new CHXSimpleList();
-
- }
- void CAudioOutUNIX::_initAfterContext()
- {
- #if defined(_THREADED_AUDIO) && defined(_UNIX_THREADS_SUPPORTED)
- HX_ASSERT( m_pContext );
-
- //Find out if the user wants to use threads or not.
- IHXPreferences* pPreferences = NULL;
- if( m_pContext && HXR_OK == m_pContext->QueryInterface( IID_IHXPreferences, (void **) &pPreferences))
- {
- IHXBuffer *pBuffer = NULL;
- pPreferences->ReadPref("ThreadedAudio", pBuffer);
- if (pBuffer)
- {
- m_bUserWantsThreads = (::atoi((const char*)pBuffer->GetBuffer()) == 1);
- HX_RELEASE(pBuffer);
- }
- HX_RELEASE( pPreferences );
- }
- if( m_bUserWantsThreads )
- {
- //Initialize the write list and playstate mutex.
- HXMutex::MakeMutex(m_mtxWriteListPlayStateLock );
- HXMutex::MakeMutex( m_mtxDeviceStateLock );
- HXThread::MakeThread( m_audioThread );
- }
- #endif
- }
- CAudioOutUNIX::~CAudioOutUNIX()
- {
- //We must assume that _Imp_Close has already been called. If not, we are
- //in big trouble.
- if ( m_wState != RA_AOS_CLOSED )
- {
- HX_ASSERT( "Device not closed in dtor." == NULL );
- }
-
- #if defined(_THREADED_AUDIO) && defined(_UNIX_THREADS_SUPPORTED)
- //Sanity check that the audio thread has exited.
- //XXXgfw ADD a thread join to HXThread and use it instead
- // of the cond var below in _close and we won't have
- // to check here at all.
- #ifdef _DEBUG
- UINT32 unThreadID = 0;
- if( m_audioThread )
- {
- m_audioThread->GetThreadId(unThreadID);
- HX_ASSERT( unThreadID == 0 );
- }
- #endif
- #endif
- //Clean up the scheduler.
- HX_RELEASE( m_pScheduler );
-
- //Just in case it isn't empty at this point.
- while( m_pWriteList && !m_pWriteList->IsEmpty() )
- {
- IHXBuffer* pBuffer = (IHXBuffer *)(m_pWriteList->RemoveHead());
- HX_RELEASE( pBuffer );
- }
- HX_DELETE(m_pPlaybackCountCBTime);
- HX_RELEASE(m_pCallback);
- HX_DELETE(m_pWriteList);
- HX_VECTOR_DELETE(m_pRollbackBuffer);
-
- #if defined(_THREADED_AUDIO) && defined(_UNIX_THREADS_SUPPORTED)
- if( m_bUserWantsThreads )
- {
- HX_DELETE( m_mtxWriteListPlayStateLock );
- HX_DELETE( m_mtxDeviceStateLock );
- HX_DELETE( m_audioThread );
- }
- #endif
- }
- UINT16 CAudioOutUNIX::_Imp_GetVolume()
- {
- if (!m_bMixerPresent)
- _OpenMixer();
- if ( m_bMixerPresent )
- {
- m_uCurVolume = _GetVolume();
- }
- return m_uCurVolume;
- }
- HX_RESULT CAudioOutUNIX::_Imp_SetVolume( const UINT16 uVolume )
- {
- HX_RESULT retCode = RA_AOE_NOERR;
-
- //Mixer methods can be called before _Imp_Open
- if( !m_bMixerPresent )
- _OpenMixer();
- //m_uCurVolume is set up at the pnaudev level.
- if( m_bMixerPresent )
- {
- retCode =_SetVolume( uVolume );
- }
- m_wLastError = retCode;
- return m_wLastError;
- }
- BOOL CAudioOutUNIX::_Imp_SupportsVolume()
- {
- return TRUE;
- }
- HX_RESULT CAudioOutUNIX::_Imp_Open( const HXAudioFormat* pFormat )
- {
- HX_RESULT retCode = RA_AOE_NOERR;
-
- //Schedule the timer callback...
- if(m_pContext && !m_pScheduler)
- {
- m_pContext->QueryInterface(IID_IHXScheduler, (void**) &m_pScheduler );
- }
- // Check state. Could already be opened.
- if( !IsOpen() && m_wState != RA_AOS_OPENING )
- {
- retCode = _OpenAudio();
- if( retCode == RA_AOE_NOERR )
- {
- m_wBlockSize = m_ulBytesPerGran;
- m_uSampFrameSize
- = pFormat->uBitsPerSample / 8;
-
- //Set device state
- LOCK(m_mtxWriteListPlayStateLock);
- m_wState = RA_AOS_OPENING;
- UNLOCK(m_mtxWriteListPlayStateLock);
- //Configure the audio device.
- retCode = _SetDeviceConfig( pFormat );
- if (retCode != RA_AOE_NOERR)
- {
- _CloseAudio();
- m_wState=RA_AOS_CLOSED;
- }
- else
- {
- #if defined(_THREADED_AUDIO) && defined(_UNIX_THREADS_SUPPORTED)
- //We want to sleep as a function of device buffer size.
- //If we have a small m_ulDeviceBufferSize we can only
- //afford to sleep just a little while.
- HX_ASSERT( m_ulDeviceBufferSize != 0 );
- m_ulSleepTime = (((float)m_ulDeviceBufferSize/(float)m_uSampFrameSize)/
- (float)m_unSampleRate) * 1000 / (float)m_unNumChannels;
- #endif
- if (!m_bMixerPresent)
- _OpenMixer();
- if( _IsSelectable() )
- {
- IHXAsyncIOSelection* pAsyncIO = NULL;
- if( m_pContext && HXR_OK == m_pContext->QueryInterface( IID_IHXAsyncIOSelection, (void**)&pAsyncIO))
- {
- pAsyncIO->Add( new HXPlaybackCountCB(this, FALSE), _Imp_GetAudioFd() , PNAIO_WRITE);
- HX_RELEASE( pAsyncIO );
- }
- }
- }
- }
- else
- {
- //Couldn't open the audio device.
- //Just pass through and return the error.
- m_wState=RA_AOS_CLOSED;
- }
-
- }
- // If we're optimizing/minimizing heap, we don't want to do this malloc
- // yet, because we're going to change/minize the value of
- // m_ulDeviceBufferSize later on.
- #ifndef HELIX_CONFIG_MIN_ROLLBACK_BUFFER
- //_SetDeviceConfig should have set the m_ulDeviceBufferSize var.
- if( RA_AOE_NOERR == retCode && !_HardwarePauseSupported() )
- {
- HX_ASSERT( m_ulDeviceBufferSize != 0 );
- if( NULL == m_pRollbackBuffer)
- {
- m_pRollbackBuffer = new UCHAR[m_ulDeviceBufferSize];
- memset( m_pRollbackBuffer, '0', m_ulDeviceBufferSize );
- }
- }
- #endif // HELIX_CONFIG_MIN_ROLLBACK_BUFFER
- #if defined(_THREADED_AUDIO) && defined(_UNIX_THREADS_SUPPORTED)
- //Start up the audio thread if it isn't going allready from a previous open
- //which sometimes happens.
-
- //Make sure we only have one thread going at a time.
- if( m_bUserWantsThreads && RA_AOE_NOERR==retCode )
- {
- UINT32 unThreadID = 0;
- m_audioThread->GetThreadId(unThreadID);
-
- if( unThreadID == 0 )
- m_audioThread->CreateThread( CAudioOutUNIX::AudioThread, this, 0 );
-
- m_audioThread->GetThreadId(unThreadID);
- }
- #endif
- m_wLastError = retCode;
- return m_wLastError;
- }
- HX_RESULT CAudioOutUNIX::_Imp_Close()
- {
- HX_RESULT retCode = RA_AOE_NOERR;
- LOCK(m_mtxWriteListPlayStateLock);
- m_wState = RA_AOS_CLOSING;
- UNLOCK(m_mtxWriteListPlayStateLock);
- #if defined(_THREADED_AUDIO) && defined(_UNIX_THREADS_SUPPORTED)
- //The audio thread should exit when the state changes to closed.
- //Wait for it to do so and clean up.
- if( m_bUserWantsThreads )
- {
- m_audioThread->Exit(0);
- }
-
- #endif
- //Reset the device and empty the write buffer.
- //Don't protect this reset with a lock as the audio thread
- //has alread exited.
- retCode = _Imp_Reset();
-
- if( retCode != RA_AOE_DEVNOTOPEN )
- {
- if( _IsSelectable() )
- {
- IHXAsyncIOSelection* pAsyncIO = NULL;
-
- if( HXR_OK == m_pContext->QueryInterface( IID_IHXAsyncIOSelection,
- (void**)&pAsyncIO)
- )
- {
- pAsyncIO->Remove(_Imp_GetAudioFd() , PNAIO_WRITE);
- HX_RELEASE( pAsyncIO );
- }
- }
- }
- //Close the audio device.
- retCode = _CloseAudio();
-
- //Close the mixer device.
- _CloseMixer();
- //Set state.
- LOCK(m_mtxWriteListPlayStateLock);
- m_wState = RA_AOS_CLOSED;
- UNLOCK(m_mtxWriteListPlayStateLock);
- //Remove callback from scheduler
- if (m_bCallbackPending)
- {
- m_pScheduler->Remove(m_PendingCallbackID);
- m_bCallbackPending = FALSE;
- }
- HX_VECTOR_DELETE(m_pRollbackBuffer);
- //return
- m_wLastError = retCode;
- return m_wLastError;
- }
- //So far no UN*X platforms support this.
- HX_RESULT CAudioOutUNIX::_Imp_Seek(ULONG32 ulSeekTime)
- {
- m_wLastError = HXR_OK;
- return m_wLastError;
- }
- HX_RESULT CAudioOutUNIX::_Imp_Pause()
- {
- //XXXgfw We really should be closing the device instead of keeping it open to be nice to
- //other procs.
- LOCK(m_mtxWriteListPlayStateLock);
- m_wState = RA_AOS_OPEN_PAUSED;
- UNLOCK(m_mtxWriteListPlayStateLock);
-
-
- if( !_HardwarePauseSupported() )
- {
- // Strategy.
- //--Find out how much we have left in the device's buffer.
- ULONG32 ulBytesPlayed = _GetBytesActualyPlayed();
- ULONG32 ulNumBytesToRewind = m_ulTotalWritten-ulBytesPlayed;
-
- //Sometimes we can get a value for bytesPlayed that is slightly
- //more then the amount we have actually written. This results
- //in a huge number.
- if( ulBytesPlayed > m_ulTotalWritten )
- ulNumBytesToRewind = 0;
-
- //--and reset player and discard all the data in the device's buffer
- //We will just ignore an error here. That means the buffer will just drain
- //and we will hear it again when they unpause.
- LOCK(m_mtxDeviceStateLock);
- _Reset();
- UNLOCK(m_mtxDeviceStateLock);
- //Make sure we only deal with full samples. Bytes-per-sample*num-channels.
- int nRem = ulNumBytesToRewind%(m_uSampFrameSize*m_unNumChannels);
- ulNumBytesToRewind = ulNumBytesToRewind>nRem ? ulNumBytesToRewind-nRem : 0;
- // In heap-optimized mode, we accept that we're going to lose a
- // little bit of data after a pause/resume because we are using as
- // small a rollback buffer as we can get away with.
- #ifdef HELIX_CONFIG_MIN_ROLLBACK_BUFFER
- if( ulNumBytesToRewind > m_ulDeviceBufferSize )
- {
- ulNumBytesToRewind = m_ulDeviceBufferSize;
- }
- #endif // HELIX_CONFIG_MIN_ROLLBACK_BUFFER
- // and add it to the front of the write buffer.
- IHXBuffer* pNewBuffer = new CHXBuffer();
- pNewBuffer->Set( m_pRollbackBuffer+m_ulDeviceBufferSize-ulNumBytesToRewind,
- ulNumBytesToRewind );
- LOCK(m_mtxWriteListPlayStateLock);
- m_pWriteList->AddHead(pNewBuffer);
- pNewBuffer->AddRef();
- UNLOCK(m_mtxWriteListPlayStateLock);
- //--Subtract that from m_ulTotalWritten.
- m_ulTotalWritten -= ulNumBytesToRewind;
-
- _Pause();
- }
- else
- {
- //The hardware device handles the Pause/Resume.
- LOCK(m_mtxDeviceStateLock);
- _Pause();
- UNLOCK(m_mtxDeviceStateLock);
- }
-
- m_wLastError = HXR_OK;
- return m_wLastError;
- }
- HX_RESULT CAudioOutUNIX::_Imp_Resume()
- {
- //XXXgfw We really should be closing and re-opening the device to be nice to other procs.
- LOCK(m_mtxWriteListPlayStateLock);
- m_wState = RA_AOS_OPEN_PLAYING;
- UNLOCK(m_mtxWriteListPlayStateLock);
- //XXXgfw If the two branches of the if are the same maybe get rid of one????
- if( !_HardwarePauseSupported() )
- {
- _Resume();
- _Imp_Write(NULL);
- }
- else
- {
- //The hardware device handles the Pause/Resume.
- _Resume();
- _Imp_Write(NULL);
- }
-
- m_wLastError = HXR_OK;
- return m_wLastError;
- }
- HX_RESULT CAudioOutUNIX::_Imp_Reset()
- {
- HX_RESULT retCode = RA_AOE_NOERR;
-
- if ( m_wState != RA_AOS_CLOSED )
- {
- LOCK(m_mtxDeviceStateLock);
- retCode = _Reset();
- UNLOCK(m_mtxDeviceStateLock);
- LOCK(m_mtxWriteListPlayStateLock);
- while (m_pWriteList && m_pWriteList->GetCount() > 0)
- {
- IHXBuffer* pBuffer = (IHXBuffer *)(m_pWriteList->RemoveHead());
- HX_RELEASE( pBuffer );
- }
- UNLOCK(m_mtxWriteListPlayStateLock);
- m_ulTotalWritten = 0;
- m_bFirstWrite = TRUE;
- m_ulLastNumBytes = 0;
- }
-
- m_wLastError = retCode;
- return m_wLastError;
- }
- HX_RESULT CAudioOutUNIX::_Imp_Drain()
- {
- HX_RESULT retCode = RA_AOE_NOERR;
- LOCK(m_mtxWriteListPlayStateLock);
- if( m_wState != RA_AOS_CLOSED )
- {
- retCode = _Drain();
- }
- while( m_pWriteList && !m_pWriteList->IsEmpty() )
- {
- IHXBuffer* pBuffer = (IHXBuffer *)(m_pWriteList->RemoveHead());
- HX_RELEASE( pBuffer );
- }
- UNLOCK(m_mtxWriteListPlayStateLock);
-
- m_wLastError = retCode;
- return m_wLastError;
- }
- HX_RESULT CAudioOutUNIX::_Imp_CheckFormat( const HXAudioFormat* pFormat )
- {
- HX_RESULT retCode = RA_AOE_NOERR;
- m_wLastError = HXR_OK;
- retCode = _CheckFormat( pFormat );
- if( RA_AOE_NOERR != retCode && RA_AOE_DEVBUSY != retCode )
- {
- m_wLastError = HXR_FAILED;
- }
- else
- {
- m_wLastError = HXR_OK;
- }
-
- return m_wLastError;
- }
- HX_RESULT CAudioOutUNIX::_Imp_GetCurrentTime( ULONG32& ulCurrentTime )
- {
- ULONG32 ulTime = 0;
- UINT64 ulBytes = 0;
- LOCK(m_mtxWriteListPlayStateLock);
- ulBytes = _GetBytesActualyPlayed();
- UNLOCK(m_mtxWriteListPlayStateLock);
-
- ulTime = (ULONG32)(( ( (double)ulBytes/(double)m_uSampFrameSize)/(double)m_unSampleRate) * 1000.0 / (double)m_unNumChannels);
-
- //Not used anywhere but belongs to CHXAudioDevice so we must set it.
- m_ulCurrentTime = ulTime;
- //Set the answer.
- ulCurrentTime = ulTime;
- m_wLastError = HXR_OK;
- return HXR_OK;
- }
- void CAudioOutUNIX::DoTimeSyncs()
- {
- ReschedPlaybackCheck();
- OnTimeSync();
- return;
- }
- HX_RESULT CAudioOutUNIX::ReschedPlaybackCheck()
- {
- HX_RESULT retCode = HXR_OK;
-
- if(!m_bCallbackPending)
- {
- HX_ASSERT( m_pCallback );
- if(m_pCallback)
- {
- *m_pPlaybackCountCBTime += (int)(500*m_ulGranularity);
- m_bCallbackPending = TRUE;
- m_PendingCallbackID = m_pScheduler->AbsoluteEnter( m_pCallback,*((HXTimeval*)m_pPlaybackCountCBTime));
- }
- else
- {
- retCode = HXR_OUTOFMEMORY;
- }
- }
- m_wLastError = retCode;
- return m_wLastError;
- }
- UINT16 CAudioOutUNIX::_NumberOfBlocksRemainingToPlay(void)
- {
- UINT32 bytesBuffered = 0;
- //We have to go through all of the buffers and count the size. Even though
- //we always write m_wBlockSize buffers, we can get off-sized buffs because
- //of the pause/resume code. When we pause we rewind however many bytes have
- //not been played yet in the buffer.
-
- LOCK(m_mtxWriteListPlayStateLock);
- if( m_pWriteList )
- {
- LISTPOSITION i = m_pWriteList->GetHeadPosition();
-
- while( i )
- {
- bytesBuffered += ((IHXBuffer *)m_pWriteList->GetAt(i)) -> GetSize();
- m_pWriteList->GetNext(i);
- }
- }
- bytesBuffered += (m_ulTotalWritten - _GetBytesActualyPlayed());
- UNLOCK(m_mtxWriteListPlayStateLock);
- return bytesBuffered / m_wBlockSize + 1;
- }
- CAudioOutUNIX::HXPlaybackCountCB::~HXPlaybackCountCB()
- {
- }
- STDMETHODIMP CAudioOutUNIX::HXPlaybackCountCB::QueryInterface( REFIID riid, void** ppvObj )
- {
- HX_RESULT retCode = HXR_OK;
-
- if( IsEqualIID(riid, IID_IHXCallback) )
- {
- AddRef();
- *ppvObj = (IHXCallback*)this;
- }
- else if (IsEqualIID(riid, IID_IUnknown))
- {
- AddRef();
- *ppvObj = this;
- }
- else
- {
- *ppvObj = NULL;
- retCode = HXR_NOINTERFACE;
- }
- return retCode;
- }
- STDMETHODIMP_(ULONG32) CAudioOutUNIX::HXPlaybackCountCB::AddRef()
- {
- return InterlockedIncrement(&m_lRefCount);
- }
- STDMETHODIMP_(ULONG32) CAudioOutUNIX::HXPlaybackCountCB::Release()
- {
-
- if (InterlockedDecrement(&m_lRefCount) > 0)
- {
- return m_lRefCount;
- }
-
- delete this;
- return HXR_OK;
- }
- STDMETHODIMP CAudioOutUNIX::HXPlaybackCountCB::Func(void)
- {
- if (m_pAudioDeviceObject)
- {
- if(!m_timed)
- {
- //m_pAudioDeviceObject->_Imp_Write(NULL);
- }
- else
- {
- m_pAudioDeviceObject->m_bCallbackPending = FALSE;
- m_pAudioDeviceObject->_Imp_Write(NULL);
- m_pAudioDeviceObject->DoTimeSyncs();
- }
- }
- return HXR_OK;
- }
- HX_RESULT CAudioOutUNIX::_Pause()
- {
- return RA_AOE_NOTSUPPORTED;
- }
- HX_RESULT CAudioOutUNIX::_Resume()
- {
- return RA_AOE_NOTSUPPORTED;
- }
- BOOL CAudioOutUNIX::_IsSelectable() const
- {
- return TRUE;
- }
- BOOL CAudioOutUNIX::_HardwarePauseSupported() const
- {
- return FALSE;
- }
- #if defined(_THREADED_AUDIO) && defined(_UNIX_THREADS_SUPPORTED)
- void* CAudioOutUNIX::AudioThread(void *thisPointer )
- {
- BOOL bReadyToExit=FALSE;
- CAudioOutUNIX* that = (CAudioOutUNIX*)thisPointer;
- while(!bReadyToExit)
- {
- //If you want to destroy frame rate just sit on this lock for
- //a while. If you don't, make sure this is a *very* fast loop.
- that->m_mtxWriteListPlayStateLock->Lock();
- that->m_mtxDeviceStateLock->Lock();
- if(that->m_wState!=RA_AOS_CLOSED && that->m_wState!=RA_AOS_CLOSING)
- {
- if( that->m_pWriteList->GetCount() > 0 && that->m_wState == RA_AOS_OPEN_PLAYING )
- {
- that->_PushBits();
- }
- }
- else
- {
- bReadyToExit=TRUE;
- }
- that->m_mtxDeviceStateLock->Unlock();
- that->m_mtxWriteListPlayStateLock->Unlock();
- //OK, sleep the amount of time it takes to play 1/4 of the device's buffer.
- microsleep(that->m_ulSleepTime/4);
- }
- //Signal the parent thread that we are done.
- that->m_audioThread->Exit(0);
- return (void*)0;
- }
- #endif
- // Don't protect anything in this method with mutexes. It has
- // already been done from where it is called from. But mostly
- // because we aren't using recursive mutexes yet.
- ULONG32 CAudioOutUNIX::_PushBits()
- {
- IHXBuffer* pBuffer = NULL;
- UCHAR* pData = 0;
- ULONG32 ulBufLen = 0;
- LONG32 lCount = 0;
- //We are going to try and write. Grab the head and do it.
- pBuffer = (IHXBuffer*)m_pWriteList->RemoveHead();
- pData = pBuffer->GetBuffer();
- ulBufLen = pBuffer->GetSize();
- //
- // Now, in case there is no mixer present, do the volume manually.
- //
- UCHAR* pNoVolumeData = NULL;
-
- if ( !m_bMixerPresent )
- {
- //Save a copy of the non-volume mutated sound data. If we don't, our
- //rewind buffer will fill up with volume modified data and it will get
- //run through the manual sound code again.
- if( !_HardwarePauseSupported() )
- {
- pNoVolumeData = new UCHAR[ ulBufLen ];
- memcpy( pNoVolumeData, pData, ulBufLen ); /* Flawfinder: ignore */
- }
-
- int i, j = ulBufLen / sizeof(short);
- short *s = (short *)pData;
- for (i = 0 ; i < j ; i++)
- {
- s[i] = ((int)s[i]) * (m_uCurVolume) / MAX_VOLUME;
- }
- }
- UNLOCK(m_mtxWriteListPlayStateLock);
- _WriteBytes(pData, ulBufLen, lCount);
- LOCK(m_mtxWriteListPlayStateLock);
- //Make sure we wrote the whole buffer like we wanted to.
- if( lCount!=-1 && lCount != ulBufLen)
- {
- //In release we can ignore this...I guess. But why didn't we?
- HX_ASSERT( "We ALWAYS write full buffers!" == NULL );
- }
-
- // Check for bad write...then check errno; write could be interrupted
- // by setitimer and sigalarm. If so, loop and try writing again.
- if ( lCount == -1 )
- {
- //EAGAIN should *never* happen as we are checking to make sure we don't
- //block on this call....
- if ( errno == EAGAIN)
- {
- HX_ASSERT( "We shouldn't be blocking here..."==NULL);
- }
- }
- if( lCount > 0 )
- {
- m_ulTotalWritten += lCount;
- //We only need to do this if the hardware doesn't support pause.
- if( !_HardwarePauseSupported() )
- {
- // If we wrote to the device we need to keep a copy of the
- // data our device buffer. We use this to 'rewind' the data
- // in case we get paused.
- //If we could write lCount without blocking then there was at
- //least that much room in the device and since m_pRollbackBuffer
- //is as large as the devices buffer, we can safely shift and copy.
- //Add the new stuff to the end pushing the rest of the data forward.
- //We need this sanity check to prevent a very hard bug to find.
- //Don't get rid of it! If this asserts it *really* means something.
- //The first thing to check is the __powerpc defines that determine
- //buffer size.
- HX_ASSERT( lCount <= m_ulDeviceBufferSize );
- // Ok, now that we've asserted to catch the
- // bug, let's prevent the crash.
- if (lCount > m_ulDeviceBufferSize) lCount = m_ulDeviceBufferSize;
- memmove( m_pRollbackBuffer, m_pRollbackBuffer+lCount, m_ulDeviceBufferSize-lCount); /* Flawfinder: ignore */
-
- //Now copy in the new data at the end of the buffer.
- if( NULL != pNoVolumeData )
- memcpy( m_pRollbackBuffer+m_ulDeviceBufferSize-lCount, pNoVolumeData, lCount ); /* Flawfinder: ignore */
- else
- memcpy( m_pRollbackBuffer+m_ulDeviceBufferSize-lCount, pData, lCount ); /* Flawfinder: ignore */
- }
- }
- HX_VECTOR_DELETE(pNoVolumeData);
- HX_RELEASE( pBuffer );
-
- return lCount;
- }
- HX_RESULT CAudioOutUNIX::_Imp_Write( const HXAudioData* pAudioOutHdr )
- {
- IHXBuffer* pBuffer = NULL;
- ULONG32 ulCount = 0;
-
- //_Imp_Write won't be called if the device couldn't be opened. But,
- //Just in case.
- if( !IsOpen() )
- {
- return RA_AOE_DEVNOTOPEN;
- }
- //Schedule callbacks.
- if( m_bFirstWrite && pAudioOutHdr )
- {
- m_bFirstWrite = FALSE;
- // Initialize the playback callback time.
- HXTimeval lTime = m_pScheduler->GetCurrentSchedulerTime();
- m_pPlaybackCountCBTime->tv_sec = lTime.tv_sec;
- m_pPlaybackCountCBTime->tv_usec = lTime.tv_usec;
- // Scheduler playback callback.
- ReschedPlaybackCheck();
- }
- //Blindly add the incoming data to the tail of the writelist.
- //Some audio devices have very small device buffers. If this
- //buffer is smaller than our incoming block size we must break
- //up the incoming buffer into smaller chuncks. We want to be
- //able to fix *at least* two(1.5?) chunks into the buffer so we don't
- //block all the time.
- if( pAudioOutHdr != NULL )
- {
- LOCK(m_mtxWriteListPlayStateLock);
- int nBuffSize = pAudioOutHdr->pData->GetSize();
- // In heap-optimized mode, this is where we set the value of
- // m_ulDeviceBufferSize, and malloc the buffer.
- #ifdef HELIX_CONFIG_MIN_ROLLBACK_BUFFER
- if( m_ulDeviceBufferSize != nBuffSize )
- {
- m_ulDeviceBufferSize = nBuffSize;
- HX_VECTOR_DELETE(m_pRollbackBuffer);
- m_pRollbackBuffer = new UCHAR[m_ulDeviceBufferSize];
- }
- #endif // HELIX_CONFIG_MIN_ROLLBACK_BUFFER
- // Shouldn't this be ">=" and not ">"?
- if( m_ulDeviceBufferSize >= nBuffSize )
- {
- //No need to break it up.
- IHXBuffer* pTmpBuff = pAudioOutHdr->pData;
- m_pWriteList->AddTail(pTmpBuff);
- pTmpBuff->AddRef();
- }
- else
- {
- //Break it up. First find the correct size.
- while( nBuffSize >= m_ulDeviceBufferSize )
- nBuffSize = nBuffSize>>1;
- //Now make sure we never break a buffer apart in the
- //middle of a Frame.
- int nRem = nBuffSize%(m_uSampFrameSize*m_unNumChannels);
- nBuffSize = nBuffSize>nRem ? nBuffSize-nRem : nBuffSize ;
- //March through the buffer, breaking it up.
- UCHAR* pData = pAudioOutHdr->pData->GetBuffer();
- int nLimit = pAudioOutHdr->pData->GetSize();
- int nPtr = 0;
- while( nPtr < nLimit )
- {
- IHXBuffer* pNewBuffer = new CHXBuffer();
- if( nPtr+nBuffSize <= nLimit )
- {
- pNewBuffer->Set( pData+nPtr, nBuffSize );
- }
- else
- {
- pNewBuffer->Set( pData+nPtr, (nLimit-nPtr) );
- }
- m_pWriteList->AddTail( pNewBuffer );
- pNewBuffer->AddRef();
- nPtr += nBuffSize;
- }
- }
- UNLOCK(m_mtxWriteListPlayStateLock);
- }
- #if defined(_THREADED_AUDIO) && defined(_UNIX_THREADS_SUPPORTED)
- //If we are using threaded audio just return and let the audio thread
- //grab the data and write it to the device.
- if( m_bUserWantsThreads )
- {
- return RA_AOE_NOERR;
- }
- #endif
- // ----------- Threaded audio note:
- //
- // We don't need to protect anything below here with the mutex
- // because we will never get here if we are running with threaded
- // audio.
- //
- //XXXgfw This doesn't seem to be doing any good anymore. So I am taking it
- // out
- // //Check for underflow here and do what? Pause it?
- // if( m_wState!=RA_AOS_OPEN_PAUSED && m_pWriteList->GetCount() <= 0 )
- // {
- // ULONG32 ulTmp = 0;
-
- // _GetRoomOnDevice(ulTmp);
- // //there better be stuff in the device because we wouldn't get this
- // //far if we were not in an OPEN/Playing state.
- // if( ulTmp >= m_ulDeviceBufferSize )
- // {
- // //XXXgfw, read below...
- // //We aren't writting anything and there is no
- // //data in the device buffer. Lions, tigers and bears..
- // //Maybe we should just do a _Pause here to keep
- // //the byte count from the audio device correct?
- // HX_ASSERT( "UNDERFLOW in AUDIO DEVICE" == NULL );
- // }
- // }
- //Just return if there is nothing to do
- if( m_pWriteList->GetCount() <= 0 || m_wState==RA_AOS_OPEN_PAUSED )
- {
- return RA_AOE_NOERR;
- }
- //
- // We only want to write to the device if we can send the
- // whole buffer. If there isn't that much room then just
- // put the buffer back in the front of the que.
- //
- ULONG32 ulBytesAvailable = 0;
- HX_RESULT code = RA_AOE_NOERR;
-
- code = _GetRoomOnDevice(ulBytesAvailable);
- if( RA_AOE_NOERR != code )
- {
- //What? Can't get the room on the device for some reason.
- m_wLastError = code;
- return m_wLastError;
- }
- //Peek at the head.
- pBuffer = (IHXBuffer*)m_pWriteList->GetHead();
- if( NULL==pBuffer || ulBytesAvailable < pBuffer->GetSize())
- {
- m_wLastError = RA_AOE_NOERR;
- return m_wLastError;
- }
-
- ulCount = _PushBits();
-
- if ( m_bFirstWrite )
- {
- m_bFirstWrite = FALSE;
-
- /* Initialize the playback callback time. */
- HXTimeval lTime = m_pScheduler->GetCurrentSchedulerTime();
- m_pPlaybackCountCBTime->tv_sec = lTime.tv_sec;
- m_pPlaybackCountCBTime->tv_usec = lTime.tv_usec;
-
- /* Scheduler playback callback. */
- ReschedPlaybackCheck();
- }
- //make sure the device is kept full.
- if( m_pWriteList->GetCount()>0 && ulCount>0 )
- _Imp_Write(NULL);
- return m_wLastError;
-
- }
- HX_RESULT CAudioOutUNIX::_CheckFormat( const HXAudioFormat* pFormat )
- {
- return RA_AOE_NOERR;
- }