www.pudn.com > helix.src.0812.rar > 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  
 
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
 
#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); 
	pNewBuffer->AddRef(); 
 
	// Update total pause bytes offset for time/video sync 
	m_ulPauseBytes += pause_bytes; 
 
	return RA_AOE_NOERR; 
} 
 
HX_RESULT CAudioOutQNX::_Imp_Resume() 
{ 
	m_paused = FALSE; 
	_Imp_Write( NULL ); 
	return RA_AOE_NOERR; 
} 
 
HX_RESULT CAudioOutQNX::_Imp_Reset() 
{ 
 
	m_ulLastTimeChecked = (UINT32) -1; 
	m_ulLastTimeReturned = 0; 
 
	if ( m_wState == RA_AOS_CLOSED ) 
		return RA_AOE_NOERR; 
 
	if ( m_wID < 0 ) 
		return RA_AOE_DEVNOTOPEN; 
 
	// Temporary FLUSH <--> DRAIN 
	if ( ioctl (m_wID, SND_PCM_IOCTL_DRAIN_PLAYBACK, 0) == -1 ) 
		return RA_AOE_GENERAL; 
 
	while (m_pWriteList && m_pWriteList->GetCount() > 0) 
	{ 
	    IHXBuffer* pBuffer = (IHXBuffer *)(m_pWriteList->RemoveHead()); 
	    pBuffer->Release(); 
	} 
 
	m_ulTotalWritten    = 0; 
	m_bFirstWrite	    = TRUE; 
	m_ulLastNumBytes    = 0; 
 
	return RA_AOE_NOERR; 
} 
 
HX_RESULT CAudioOutQNX::_Imp_Drain() 
{ 
	if ( m_wID < 0 ) 
		return RA_AOE_DEVNOTOPEN; 
 
	// Temporary FLUSH <--> DRAIN 
	if ( ioctl (m_wID, SND_PCM_IOCTL_FLUSH_PLAYBACK, 0) == -1 ) 
		return RA_AOE_GENERAL; 
 
	return RA_AOE_NOERR; 
} 
 
AUDIOERROR CAudioOutQNX::SetDeviceConfig 
(  
	const HXAudioFormat* pFormat  
) 
{ 
	if ( m_wID < 0 )  
		return RA_AOE_NOTENABLED; 
 
	int sampleWidth      = pFormat->uBitsPerSample; 
	ULONG32 sampleRate   = pFormat->ulSamplesPerSec; 
	int numChannels      = pFormat->uChannels; 
	m_wBlockSize         = m_ulBytesPerGran;  //pFormat->uMaxBlockSize; 
 
	ULONG32 bufSize = 128; 
	ULONG32 bytesPerBlock = m_wBlockSize;  
	while ( bufSize < 4096 ) 
	{ 
		bufSize *= 2; 
	} 
 
	m_ulFragSize = bufSize; 
 
	snd_pcm_playback_params_t playback_params; 
	memset( &playback_params, 0, sizeof(playback_params) ); 
	playback_params.fragment_size  = m_ulFragSize; 
	playback_params.fragments_max  = -1; 
	playback_params.fragments_room =  1; 
 
	/* it's okay to fail, card may not handle fragment size */ 
	ioctl(m_wID, SND_PCM_IOCTL_PLAYBACK_PARAMS, &playback_params ); 
 
	snd_pcm_format_t format; 
	memset( &format, 0, sizeof(format)); 
 
	if( sampleWidth == 16 ) 
	{ 
	    format.format = SND_PCM_SFMT_S16_LE; 
	} 
	else  
	{ 
	    format.format = SND_PCM_SFMT_U8; 
	    m_uSampFrameSize /= 2; 
	} 
 
	format.channels		= numChannels; 
	format.rate			= sampleRate; 
 
	m_sample_rate		= sampleRate; 
	m_num_channels		= numChannels; 
	m_uSampFrameSize	= sampleWidth / 8; 
 
	if(ioctl(m_wID, SND_PCM_IOCTL_PLAYBACK_FORMAT, &format) == -1) 
	{ 
	    return (  m_wLastError = RA_AOE_NOTENABLED ); 
	} 
 
	numChannels = format.channels; 
	sampleRate  = format.rate; 
 
	// 
	// Verify that requested format was accepted by the audio device. 
	// 
	if ( numChannels != pFormat->uChannels ) 
		((HXAudioFormat*)pFormat)->uChannels = numChannels; 
		 
	if ( sampleRate != pFormat->ulSamplesPerSec ) 
		((HXAudioFormat*)pFormat)->ulSamplesPerSec = sampleRate; 
 
	// Get the audio driver's buffer size for our rollback buffer 
    snd_pcm_playback_info_t pinfo; 
	memset( &pinfo, 0, sizeof( pinfo ) ); 
 
	if(ioctl(m_wID, SND_PCM_IOCTL_PLAYBACK_INFO, &pinfo) != -1) 
		m_ulDeviceBufferSize = pinfo.buffer_size;	 
 
	return RA_AOE_NOERR; 
} 
 
HX_RESULT CAudioOutQNX::_Imp_CheckFormat 
(  
	const HXAudioFormat* pFormat  
) 
{ 
 
	// QNX audio driver can do all formats that we our 
	// currently interested in. However, we should check 
	// for valid inputs. 
	if ( pFormat->uChannels != 1 && pFormat->uChannels != 2 ) 
		return HXR_FAILED; 
	if ( pFormat->uBitsPerSample != 8 && pFormat->uBitsPerSample != 16 ) 
		return HXR_FAILED; 
 
	// Ask driver later 
#if 0 
  /*No reason why the driver won't accept other sampling rates...*/ 
	if ( pFormat->ulSamplesPerSec != 8000 && pFormat->ulSamplesPerSec != 9600  && 
	     pFormat->ulSamplesPerSec != 11025 && pFormat->ulSamplesPerSec != 16000  && 
	     pFormat->ulSamplesPerSec != 18900 && pFormat->ulSamplesPerSec != 22050  && 
	     pFormat->ulSamplesPerSec != 32000 && pFormat->ulSamplesPerSec != 44100  && 
	     pFormat->ulSamplesPerSec != 48000 ) 
		return HXR_FAILED; 
#endif 
 
	return HXR_OK; 
} 
 
/************************************************************************ 
 *  Method: 
 *              CAudioOutQNX::_Imp_GetCurrentTime 
 *      Purpose: 
 *              Get the current time from the audio device. 
 *		We added this to support the clock available in the 
 *		Window's audio driver. 
 */ 
HX_RESULT CAudioOutQNX::_Imp_GetCurrentTime ( ULONG32& ulCurrentTime ) 
{ 
 
    ULONG32 ulTime   = 0; 
    ULONG32 ulBytes  = GetPlaybackBytes(); 
 
	ulBytes += m_ulPauseBytes; 
     
    ulTime = (UINT32)((  (double)(ulBytes/m_uSampFrameSize)/(double)m_sample_rate) * 1000 / m_num_channels); 
     
    //Not used anywhere but belongs to CHXAudioDevice so we must set it. 
    m_ulCurrentTime  = ulTime; 
 
    //Set the answer. 
    ulCurrentTime = ulTime; 
 
    return HXR_OK; 
} 
 
/************************************************************************ 
 *  Method: 
 *              CAudioOutQNX::_Imp_GetAudioFd 
 *      Purpose: 
 */ 
INT16 CAudioOutQNX::_Imp_GetAudioFd() 
{ 
    return m_wID; 
} 
 
/************************************************************************ 
 *  Method: 
 *              CAudioOutQNX::DoTimeSyncs 
 *      Purpose: 
 *		Manual time syncs! Fork! 
 */ 
void CAudioOutQNX::DoTimeSyncs() 
{ 
    ReschedPlaybackCheck(); 
	OnTimeSync(); 
 
    return; 
} 
 
/************************************************************************ 
 *  Method: 
 *              CAudioOutQNX::GetPlaybackBytes 
 *      Purpose: 
 *		Get the number of bytes played since last open() was 
 *		called. This ioctl() returns funky values sometimes?!@% 
 */ 
ULONG32 CAudioOutQNX::GetPlaybackBytes() 
{ 
 
	snd_pcm_playback_status_t info; 
	memset( &info,0, sizeof(info)); 
 
	if ( ioctl (m_wID, SND_PCM_IOCTL_PLAYBACK_STATUS, &info) != -1 ) 
		return info.scount; 
 
	// If ioctl failed, just guess the value 
    int bytes = m_ulTotalWritten - m_ulGranularity / 2; 
    if (bytes < 0)  
		bytes = 0; 
 
	return (ULONG32) bytes;  
} 
 
ULONG32 CAudioOutQNX::_GetPlaybackBuffer( ) 
{ 
	return( m_ulTotalWritten - GetPlaybackBytes( ) ); 
} 
 
/************************************************************************ 
 *  Method: 
 *              CAudioOutQNX::ReschedPlaybackCheck() 
 *      Purpose: 
 *		Reschedule playback callback. 
 */ 
HX_RESULT CAudioOutQNX::ReschedPlaybackCheck() 
{ 
    HX_RESULT theErr = HXR_OK; 
    if (m_bCallbackPending) 
		return theErr; 
    /* Put this back in the scheduler. 
     */ 
    HXPlaybackCountCb* pCallback = 0; 
    pCallback = new HXPlaybackCountCb; 
    if (pCallback) 
    { 
        *m_pPlaybackCountCBTime += (int) (1000 * m_ulGranularity); 
        pCallback->m_pAudioDeviceObject = this; 
        m_bCallbackPending = TRUE; 
        m_PendingCallbackID = m_pScheduler->AbsoluteEnter(pCallback, 
                        *((HXTimeval*) m_pPlaybackCountCBTime)); 
    } 
    else 
    { 
        theErr = HXR_OUTOFMEMORY; 
    } 
 
    return HXR_OK; 
} 
 
UINT16	CAudioOutQNX::_NumberOfBlocksRemainingToPlay(void) 
{ 
    UINT32 			bytesBuffered = 0; 
    LISTPOSITION	i = m_pWriteList->GetHeadPosition(); 
 
    while (i) 
    { 
		bytesBuffered += ((IHXBuffer *)m_pWriteList->GetAt(i)) -> GetSize(); 
		m_pWriteList->GetNext(i); 
    } 
     
    bytesBuffered += (m_ulTotalWritten - GetPlaybackBytes()); 
 
    return bytesBuffered / m_wBlockSize + 1; 
} 
 
 
// CAudioOutQNX::HXPlaybackCountCb 
 
CAudioOutQNX::HXPlaybackCountCb::HXPlaybackCountCb(BOOL timed) : 
     m_lRefCount (0) 
    ,m_pAudioDeviceObject (0) 
    ,m_timed(timed) 
{ 
} 
 
CAudioOutQNX::HXPlaybackCountCb::~HXPlaybackCountCb() 
{ 
} 
 
/* 
 * IUnknown methods 
 */ 
///////////////////////////////////////////////////////////////////////// 
//      Method: 
//              IUnknown::QueryInterface 
//      Purpose: 
//              Implement this to export the interfaces supported by your  
//              object. 
// 
STDMETHODIMP CAudioOutQNX::HXPlaybackCountCb::QueryInterface 
(	REFIID riid, void** ppvObj ) 
{ 
    if (IsEqualIID(riid, IID_IHXCallback)) 
    { 
        AddRef(); 
        *ppvObj = (IHXCallback*)this; 
        return HXR_OK; 
    } 
    else if (IsEqualIID(riid, IID_IUnknown)) 
    { 
        AddRef(); 
        *ppvObj = this; 
        return HXR_OK; 
    } 
 
    *ppvObj = NULL; 
    return HXR_NOINTERFACE; 
} 
 
///////////////////////////////////////////////////////////////////////// 
//      Method: 
//              IUnknown::AddRef 
//      Purpose: 
//              Everyone usually implements this the same... feel free to use 
//              this implementation. 
// 
STDMETHODIMP_(ULONG32) CAudioOutQNX::HXPlaybackCountCb::AddRef() 
{ 
    return InterlockedIncrement(&m_lRefCount); 
} 
 
///////////////////////////////////////////////////////////////////////// 
//      Method: 
//              IUnknown::Release 
//      Purpose: 
//              Everyone usually implements this the same... feel free to use 
//              this implementation. 
// 
STDMETHODIMP_(ULONG32) CAudioOutQNX::HXPlaybackCountCb::Release() 
{ 
    if (InterlockedDecrement(&m_lRefCount) > 0) 
    { 
        return m_lRefCount; 
    } 
 
    delete this; 
    return 0; 
} 
 
/* 
 *      IHXPlaybackCountCb methods 
 */ 
STDMETHODIMP CAudioOutQNX::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; 
} 
 
/************************************************************************ 
 *  Method: 
 *              CAudioOutQNX::BuffersEmpty 
 *      Purpose: 
 */ 
BOOL CAudioOutQNX::BuffersEmpty() 
{ 
    snd_pcm_playback_status_t info; 
 
    if ( -1 == ioctl(m_wID, SND_PCM_IOCTL_PLAYBACK_STATUS, &info) ) 
		return FALSE; 
 
    if ( info.queue ) 
		return FALSE; 
 
    return TRUE; 
} 
 
 
 
void CAudioOutQNX::OpenMixer() 
{ 
	int								i; 
	struct snd_mixer_info_t 		info; 
	struct snd_mixer_channel_info_t cinfo; 
 
    // 
    // return if the mixer is already opened 
    // 
    if (m_bMixerPresent) 
		return; 
 
    mixm_wID = open ( m_DevCtlName, O_RDWR ); 
 
	if ( -1 == ioctl( mixm_wID, SND_MIXER_IOCTL_INFO, &info ) ) 
	{ 
		CloseMixer( ); 
		return; 
	} 
     
    if (mixm_wID > 0) 
    { 
		/* find pcm channel */ 
		memset( &cinfo, 0, sizeof( cinfo ) ); 
		for ( i = 0; i < info.channels; i++ ) 
		{ 
			cinfo.channel = i; 
			if ( -1 == ioctl( mixm_wID, SND_MIXER_IOCTL_CHANNEL_INFO, &cinfo ) ) 
			{ 
				continue; 
			} 
			if ( 0 == strcmp( (char *)cinfo.name, SND_MIXER_ID_PCM ) )  
			{ 
				m_wPCMChannel = i; 
				break; 
			} 
		} 
 
	} 
 
	if ( m_wPCMChannel > 0 ) 
	{ 
		m_bMixerPresent = 1; 
    } 
    else 
    { 
		CloseMixer( ); 
		m_bMixerPresent = 0; 
    } 
} 
 
 
void CAudioOutQNX::CloseMixer() 
{ 
    // Close the mixer device. 
    if ( mixm_wID >= 0 )  
    { 
		close ( mixm_wID ); 
		mixm_wID = -1; 
    } 
}