📄 audqnx.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 ***** */
/*******************************************************************
*
* audqnx.cpp
*
* CLASS: CAudioOutQNX
*
* DESCRIPTION: Class implementation for QNX-specific audio devices
*
*******************************************************************/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/asound.h>
#include "hxcom.h"
#include "hxresult.h"
#include "hxengin.h"
#include "ihxpckts.h"
#include "hxslist.h"
#include "hxstrutl.h"
#include "timeval.h"
#include "audqnx.h"
#include "hxaudses.h"
#include "hxtick.h"
#include "chxpckts.h"
#include "debug.h"
struct IHXCallback;
CAudioOutQNX::CAudioOutQNX() :
m_wID( -1 ),
mixm_wID( -1 ),
m_wPCMChannel( -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(0),
m_PendingCallbackID (0),
m_bCallbackPending(FALSE),
m_paused(FALSE),
m_pWriteList(NULL),
m_last_audio_time(0),
m_ulPauseBytes(0),
m_ulDeviceBufferSize(0),
m_pRollbackBuffer(NULL)
{
// Use Photon registry later
// 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.
if ( adev )
{
SafeStrCpy( m_DevName, adev, DEVICE_NAME_SIZE );
}
else
{
SafeStrCpy( m_DevName, "/dev/pcm00", DEVICE_NAME_SIZE ); // default
}
if ( mdev )
{
SafeStrCpy( m_DevCtlName, mdev, DEVICE_NAME_SIZE );
}
else
{
SafeStrCpy( m_DevCtlName, "/dev/mixer00", DEVICE_NAME_SIZE ); // default for volume
}
m_pPlaybackCountCBTime = new Timeval;
m_pWriteList = new CHXSimpleList();
}
CAudioOutQNX::~CAudioOutQNX()
{
// Check to make sure device is closed
if ( m_wState != RA_AOS_CLOSED )
{
HX_ASSERT( "Device not closed in dtor." == NULL );
_Imp_Close();
}
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_pWriteList );
HX_VECTOR_DELETE( m_pRollbackBuffer );
}
UINT16 CAudioOutQNX::_Imp_GetVolume()
{
struct snd_mixer_channel_direction_t cdata;
if (!m_bMixerPresent)
OpenMixer();
if ( !m_bMixerPresent )
return m_uCurVolume;
cdata.channel = m_wPCMChannel;
if ( ioctl( mixm_wID, SND_MIXER_IOCTL_CHANNEL_OREAD, &cdata ) == -1 )
return (0);
m_uCurVolume = cdata.volume ;
return m_uCurVolume;
}
HX_RESULT CAudioOutQNX::_Imp_SetVolume( UINT16 uVolume )
{
struct snd_mixer_channel_direction_t cdata;
if (!m_bMixerPresent)
OpenMixer();
if ( !m_bMixerPresent )
return RA_AOE_NOERR;
cdata.channel = m_wPCMChannel;
if ( ioctl( mixm_wID, SND_MIXER_IOCTL_CHANNEL_OREAD, &cdata ) == -1 )
return ( m_wLastError = RA_AOE_NOTSUPPORTED );
cdata.volume = uVolume;
if ( ioctl( mixm_wID, SND_MIXER_IOCTL_CHANNEL_OWRITE, &cdata ) == -1 )
return ( m_wLastError = RA_AOE_NOTSUPPORTED );
return RA_AOE_NOERR;
}
BOOL CAudioOutQNX::_Imp_SupportsVolume()
{
return TRUE;
}
HX_RESULT CAudioOutQNX:: _Imp_Open ( const HXAudioFormat* pFormat )
{
printf( "_imp_open\n" );
m_ulLastTimeChecked = (UINT32) -1;
m_ulLastTimeReturned = 0;
// Get the core scheduler interface; Use this to schedule polling
// the audio device for number of bytes played.
#if 0
if ( m_pOwner )
{
m_pOwner->GetScheduler( &m_pScheduler );
m_pScheduler->AddRef();
}
#else
// 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 );
}
#endif
// 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;
// Open audio device.
if ( m_wID < 0 )
m_wID = open ( m_DevName, O_WRONLY | O_NONBLOCK );
if ( m_wID < 0 )
return ( m_wLastError = RA_AOE_BADOPEN );
m_wBlockSize = m_ulBytesPerGran; //pFormat->uMaxBlockSize;
m_uSampFrameSize = pFormat->uBitsPerSample / 8;
// Set device state
m_wState = RA_AOS_OPENING;
// Configure the audio device.
AUDIOERROR iVal = SetDeviceConfig( pFormat );
if (iVal != RA_AOE_NOERR)
{
close ( m_wID );
m_wID = -1;
return iVal;
}
// Find out if mixer is there.. the mixer controls volume.
// If there is no mixer device, then we handle volume manually by
// multiplying the samples by the volume level in the Write() method.
if (!m_bMixerPresent)
OpenMixer();
IHXAsyncIOSelection* pAsyncIO = NULL;
if( HXR_OK == m_pContext->QueryInterface(IID_IHXAsyncIOSelection, (void**)&pAsyncIO))
{
pAsyncIO->Add(new HXPlaybackCountCb(FALSE), m_wID, PNAIO_WRITE);
HX_RELEASE( pAsyncIO );
}
HX_ASSERT( m_ulDeviceBufferSize != 0 );
if( NULL == m_pRollbackBuffer)
{
m_pRollbackBuffer = new UCHAR[m_ulDeviceBufferSize];
memset( m_pRollbackBuffer, '0', m_ulDeviceBufferSize );
}
return RA_AOE_NOERR;
}
HX_RESULT CAudioOutQNX::_Imp_Close()
{
printf( "_imp_close\n" );
m_wState = RA_AOS_CLOSING;
/* reset pause offset */
m_ulPauseBytes = 0;
_Imp_Reset( );
// Close the audio device.
if ( m_wID >= 0 )
{
close ( m_wID );
IHXAsyncIOSelection* pAsyncIO;
if( HXR_OK == m_pContext->QueryInterface(IID_IHXAsyncIOSelection, (void**)&pAsyncIO))
{
pAsyncIO->Remove(m_wID, PNAIO_WRITE);
pAsyncIO->Release();
}
m_wID = -1;
}
CloseMixer();
m_wState = RA_AOS_CLOSED;
// Remove callback from scheduler
if (m_bCallbackPending)
{
m_pScheduler->Remove(m_PendingCallbackID);
m_bCallbackPending = FALSE;
}
HX_VECTOR_DELETE( m_pRollbackBuffer );
return RA_AOE_NOERR;
}
HX_RESULT CAudioOutQNX::_Imp_Write ( const HXAudioData* pAudioOutHdr )
{
IHXBuffer* pBuffer = NULL;
UCHAR* pData = 0;
ULONG32 ulBufLen = 0;
// 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();
}
if ( m_paused )
{
if ( !pAudioOutHdr )
return 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 = TRUE;
do
{
bWroteSomething = FALSE;
if(m_pWriteList->GetCount() <= 0)
{
if(!pAudioOutHdr)
return RA_AOE_NOERR;
pData = (UCHAR*)pAudioOutHdr->pData->GetBuffer();
ulBufLen = pAudioOutHdr->pData->GetSize();
}
else
{
if(pAudioOutHdr)
{
IHXBuffer* pNewBuffer = new CHXBuffer();
pNewBuffer->Set(pAudioOutHdr->pData->GetBuffer(),
pAudioOutHdr->pData->GetSize());
m_pWriteList->AddTail(pNewBuffer);
pNewBuffer->AddRef();
}
pBuffer = (IHXBuffer*)m_pWriteList->RemoveHead();
pData = pBuffer->GetBuffer();
ulBufLen = pBuffer->GetSize();
}
// Write audio data to device.
int count = 0;
count = write(m_wID, pData, ulBufLen);
if ( count == -1 )
{
// Rebuffer the data
IHXBuffer* pNewBuffer = new CHXBuffer( );
pNewBuffer->AddRef( );
pNewBuffer->Set( pData, ulBufLen );
m_pWriteList->AddHead( pNewBuffer );
}
// anything that is left over must be added to the write list at
// the beginning
if (count != -1 && count != ulBufLen)
{
// replace the extra data in the writelist
IHXBuffer* pNewBuffer = new CHXBuffer();
pNewBuffer->Set(pData + count, ulBufLen - count);
m_pWriteList->AddHead(pNewBuffer);
pNewBuffer->AddRef();
}
if (count != -1)
{
bWroteSomething = TRUE;
m_ulTotalWritten += count;
// 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 ulCount 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.
// Throw an assert here
HX_ASSERT(count <= m_ulDeviceBufferSize);
// Now protect against a crash
if (count > m_ulDeviceBufferSize) count = m_ulDeviceBufferSize;
memmove( m_pRollbackBuffer, m_pRollbackBuffer+count, m_ulDeviceBufferSize-count);
memcpy( m_pRollbackBuffer+m_ulDeviceBufferSize-count, pData, count ); /* Flawfinder: ignore */
}
HX_RELEASE( pBuffer );
pBuffer = NULL;
pAudioOutHdr = NULL; // Don't add the same buffer again
} while( bWroteSomething );
return RA_AOE_NOERR;
}
HX_RESULT CAudioOutQNX::_Imp_Seek(ULONG32 ulSeekTime)
{
return HXR_OK;
}
HX_RESULT CAudioOutQNX::_Imp_Pause()
{
m_paused = TRUE;
// Find out how much we have left in the device's buffer.
int pause_bytes = GetPlaybackBytes( );
ULONG32 ulNumBytesToRewind = m_ulTotalWritten - pause_bytes;
// Reset player and discard all the data in the device's buffer
if( _Imp_Reset() != RA_AOE_NOERR )
{
//We will just ignore it. That means the buffer will just drain
//and we will hear it again when they unpause.
}
// Add it to the front of the write buffer.
IHXBuffer* pNewBuffer = new CHXBuffer();
// Make sure we only deal with full samples. Bytes-per-sample*num-channels.
int nRem = ulNumBytesToRewind % (m_uSampFrameSize * m_num_channels);
ulNumBytesToRewind -= nRem;
pNewBuffer->Set( m_pRollbackBuffer+m_ulDeviceBufferSize-ulNumBytesToRewind, ulNumBytesToRewind );
m_pWriteList->AddHead(pNewBuffer);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -