📄 audunix.cpp
字号:
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;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -