www.pudn.com > helix.src.0812.rar > audbeos.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 ***** */  
 
/******************************************************************* 
 *	audbeos.cpp 
 * 
 *	CLASS: CAudioOutBeOS 
 *	 
 *	DESCRIPTION: Class implementation for BeOS-specific audio devices  
 *	 
 *******************************************************************/ 
 
#include "hxtypes.h" 
#include "hxresult.h" 
#include "hxcom.h" 
#include "ihxpckts.h" 
#include "hxausvc.h" 
#include "auderrs.h" 
#include "hxaudev.h" 
#include "hxtick.h" 
#include "hxthread.h" 
#include "hxbuffer.h" 
#include "hxslist.h" 
#include "hxengin.h" 
#include "timeval.h" 
#include "hxaudses.h" 
#include "audbeos.h" 
 
#include  
#include  
 
struct IHXCallback;  // forward declaration needed for callback. 
 
CAudioOutBeOS::CAudioOutBeOS() 
    : m_player(NULL) 
    , m_bFirstWrite(TRUE) 
    , m_pPendingBufferList(NULL) 
    , m_ulLastPlayCursor(0) 
    , m_ulCurrentPlayTime(0) 
    , m_ulLastTimeSync(0) 
    , m_bPlaying(FALSE) 
	, m_ulBufferSize(0) 
	, m_ulFrameSize(0) 
	, m_ulNextWriteOffset(0) 
	, m_ulOldBytesLeft(0) 
	, m_ulBlockSize(0) 
	, m_bGotAWrite(FALSE) 
#if _BEOS_AUDIODEV_CALLBACK 
	, m_pPlaybackCountCBTime(NULL) 
	, m_bCallbackPending(FALSE) 
	, m_PendingCallbackID(0) 
#endif 
{ 
#if _BEOS_AUDIODEV_CALLBACK 
	m_pPlaybackCountCBTime = new Timeval; 
#endif 
}		    
 
CAudioOutBeOS::~CAudioOutBeOS() 
{ 
	_Imp_Close(); 
#if _BEOS_AUDIODEV_CALLBACK 
	delete m_pPlaybackCountCBTime; 
#endif 
} 
 
HX_RESULT  
CAudioOutBeOS::_Imp_Open(const HXAudioFormat* pFormat) 
{ 
	size_t	bufSize; 
	void	*basePtr; 
 
    SetFormat(pFormat); 
 
#if _BEOS_AUDIODEV_CALLBACK 
	if (m_pOwner) 
	{ 
		m_pOwner->GetScheduler(&m_pScheduler); 
		m_pScheduler->AddRef(); 
	} 
#endif 
 
	// set up the player with enough buffer space for about 4 seconds of audio 
//	m_player = new BPushGameSound(m_gameSoundFormat.frame_rate, &m_gameSoundFormat, 4); 
 
	// BPushGameSound->CurrentPosition is accurate to within a buffer 
	// Therefore, to keep good synchronization between audio and video, a single 
	// buffer should not have a longer duration than a frame of video. We don't 
	// know here what the video framerate (if any) may be, but we can assume that it 
	// isn't over 30fps. Thus, each audio buffer should be no longer than about 30 ms. 
	// To store about 3 seconds of audio, we'll need 100 buffers. 
	bufSize = (size_t)(m_gameSoundFormat.frame_rate * 0.03f); 
	m_player = new BPushGameSound(bufSize, &m_gameSoundFormat, 100); 
 
	if (m_player->InitCheck() != B_OK) 
	{ 
		delete m_player; 
		m_player = NULL; 
		return RA_AOE_BADOPEN; 
	} 
	if (m_player->LockForCyclic(&basePtr, &bufSize) != BPushGameSound::lock_ok) 
		m_player->UnlockCyclic(); 
	m_ulBufferSize = bufSize; 
	m_ulBlockSize = m_ulBytesPerGran; 
 
	return HXR_OK; 
} 
 
HX_RESULT  
CAudioOutBeOS::_Imp_Close() 
{ 
#if _BEOS_AUDIODEV_CALLBACK 
	if (m_pScheduler) 
	{ 
		// Remove callback from scheduler 
		if (m_bCallbackPending) 
		{ 
			m_pScheduler->Remove(m_PendingCallbackID); 
			m_bCallbackPending = FALSE; 
		} 
		m_pScheduler->Release(); 
		m_pScheduler = NULL; 
	} 
#endif 
 
	if (m_pPendingBufferList) 
	{ 
		while (!m_pPendingBufferList->IsEmpty()) 
		{ 
			IHXBuffer	*pBuffer = (IHXBuffer *)m_pPendingBufferList->RemoveHead(); 
			pBuffer->Release(); 
			pBuffer = NULL; 
		} 
 
		delete m_pPendingBufferList; 
		m_pPendingBufferList = NULL; 
	} 
 
	if (m_player) 
	{ 
		m_player->StopPlaying(); 
		delete m_player; 
		m_player = NULL; 
	} 
	m_bPlaying = FALSE; 
    return HXR_OK; 
} 
 
HX_RESULT  
CAudioOutBeOS::_Imp_Seek(ULONG32 ulSeekTime) 
{ 
    return HXR_OK; 
} 
 
HX_RESULT  
CAudioOutBeOS::_Imp_Pause() 
{ 
	if (m_player) 
	{ 
		size_t	curPos = m_player->CurrentPosition() * m_ulFrameSize; 
		uint8	*pAudioPtr; 
		size_t	i, ulAudioBytes; 
 
		m_player->StopPlaying(); 
		if (m_player->LockForCyclic((void **)&pAudioPtr, &ulAudioBytes) == BPushGameSound::lock_ok && pAudioPtr) 
		{ 
			// shift the audio data all back to the beginning of the buffer 
			if (m_ulNextWriteOffset > curPos) 
			{ 
				for (i = curPos; i < m_ulNextWriteOffset; i++) 
				{ 
					pAudioPtr[i - curPos] = pAudioPtr[i]; 
				} 
				m_ulNextWriteOffset = m_ulNextWriteOffset - curPos; 
			} 
			else 
			{ // wrap-around 
				uint32	remaining = ulAudioBytes - curPos; 
				uint8	*tempBuf = new uint8[m_ulNextWriteOffset]; 
 
				for (i = 0; i < m_ulNextWriteOffset; i++) 
				{ 
					tempBuf[i] = pAudioPtr[i]; 
				} 
				for (i = curPos; i < ulAudioBytes; i++) 
				{ 
					pAudioPtr[i - curPos] = pAudioPtr[i]; 
				} 
				for (i = 0; i < m_ulNextWriteOffset; i++) 
				{ 
					pAudioPtr[i + remaining] = tempBuf[i]; 
				} 
 
				delete [] tempBuf; 
				m_ulNextWriteOffset = remaining + m_ulNextWriteOffset; 
			} 
			m_player->UnlockCyclic(); 
			m_bGotAWrite = TRUE; 
			m_ulLastPlayCursor = 0; 
		} 
	} 
 
	m_bPlaying = FALSE; 
	return HXR_OK; 
} 
 
HX_RESULT  
CAudioOutBeOS::_Imp_Resume() 
{ 
	if (m_player) 
		m_player->StartPlaying(); 
 
	m_ulLastPlayCursor = 0; 
	m_bPlaying = TRUE; 
	m_ulLastTimeSync = HX_GET_TICKCOUNT(); 
	OnTimeSync(); 
 
	return HXR_OK; 
} 
 
HX_RESULT  
CAudioOutBeOS::_Imp_Write(const HXAudioData* pAudioOutData) 
{ 
	HX_RESULT	theErr = HXR_OK; 
	IHXBuffer	*pBuffer = NULL; 
 
#if _BEOS_AUDIODEV_CALLBACK 
	if (m_bFirstWrite && pAudioOutData) 
	{ 
		/*  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(); 
	} 
#endif 
 
	if (pAudioOutData && pAudioOutData->pData) 
	{ 
		pAudioOutData->pData->AddRef(); 
	} 
 
	if (m_pPendingBufferList && m_pPendingBufferList->GetCount() > 0) 
	{ 
		if (pAudioOutData && pAudioOutData->pData) 
			m_pPendingBufferList->AddTail(pAudioOutData->pData); 
		pBuffer = (IHXBuffer *)m_pPendingBufferList->RemoveHead(); 
	} 
	else 
	{ 
		if (pAudioOutData && pAudioOutData->pData) 
			pBuffer = pAudioOutData->pData; 
	} 
 
    BOOL    bCanContinue = TRUE; 
 
	while (bCanContinue && pBuffer) 
	{ 
		UINT32 ulBufferSize = pBuffer->GetSize(); 
 
		uint8* pAudioPtr1	= NULL; 
		uint8* pAudioPtr2	= NULL; 
		size_t curPos; 
		size_t ulAudioBytes1 = 0; 
		size_t ulAudioBytes2 = 0; 
		size_t ulAudioBytesWritten1 = 0; 
		size_t ulAudioBytesWritten2 = 0; 
 
		if (m_player->LockForCyclic((void **)&pAudioPtr1, &ulAudioBytes1) == BPushGameSound::lock_ok && pAudioPtr1) 
		{ 
			size_t	ulSizeToWrite, curPos; 
 
			if (m_player->IsPlaying()) 
				curPos = m_player->CurrentPosition() * m_ulFrameSize; 
			else 
				curPos = 0; 
 
			if (m_ulNextWriteOffset >= curPos) 
			{ 
				pAudioPtr2 = pAudioPtr1; 
				ulAudioBytes1 -= m_ulNextWriteOffset; 
			} 
			else 
			{ 
				pAudioPtr2 = NULL; 
				ulAudioBytes1 = curPos - m_ulNextWriteOffset - 1; 
			} 
			pAudioPtr1 += m_ulNextWriteOffset; 
 
			if (ulAudioBytes1 < 0) 
				ulAudioBytes1 = 0; 
 
			ulSizeToWrite = ulBufferSize; 
			if (ulSizeToWrite > ulAudioBytes1) 
			{ 
				ulSizeToWrite = ulAudioBytes1; 
			} 
 
			if (ulSizeToWrite > 0) 
			{ 
				::memcpy(pAudioPtr1, (void*) pBuffer->GetBuffer(), ulSizeToWrite); /* Flawfinder: ignore */ 
			} 
			ulAudioBytesWritten1 = ulSizeToWrite; 
 
			size_t	ulRemainingToWrite = ulBufferSize - ulSizeToWrite; 
 
			ulAudioBytes2 = curPos - 1; 
			if (ulRemainingToWrite > 0 && pAudioPtr2 && ulAudioBytes2 > 0) 
			{ 
				ulSizeToWrite = ulRemainingToWrite; 
				if (ulSizeToWrite > ulAudioBytes2) 
				{ 
					ulSizeToWrite = ulAudioBytes2; 
				} 
				::memcpy(pAudioPtr2, (void*) (pBuffer->GetBuffer() + ulAudioBytesWritten1), ulSizeToWrite); 
				ulAudioBytesWritten2 = ulSizeToWrite; 
				ulRemainingToWrite -= ulSizeToWrite; 
			} 
 
			if (ulRemainingToWrite > 0) 
			{ 
				IHXBuffer* pNewBuffer = (IHXBuffer*) new CHXBuffer; 
				pNewBuffer->AddRef(); 
				pNewBuffer->Set(pBuffer->GetBuffer() + (ulBufferSize - ulRemainingToWrite), ulRemainingToWrite); 
 
				if (!m_pPendingBufferList) 
				{ 
					m_pPendingBufferList = new CHXSimpleList; 
				} 
 
				m_pPendingBufferList->AddHead((void*) pNewBuffer); 
 
				// no more space in the secondary buffer 
				bCanContinue = FALSE; 
			} 
 
			m_player->UnlockCyclic(); 
 
			m_ulNextWriteOffset	+= ulAudioBytesWritten1 + ulAudioBytesWritten2; 
			if (pAudioOutData) 
				m_bGotAWrite = TRUE; 
 
			if (m_ulNextWriteOffset >= m_ulBufferSize) 
			{ 
				m_ulNextWriteOffset -= m_ulBufferSize; 
			} 
 
			if (m_bFirstWrite) 
			{ 
				m_bFirstWrite = FALSE; 
			} 
		} // lock 
		else 
		{ 
			bCanContinue = FALSE; 
			if (!m_pPendingBufferList) 
			{ 
				m_pPendingBufferList = new CHXSimpleList; 
			} 
			 
			pBuffer->AddRef(); 
			m_pPendingBufferList->AddHead((void*) pBuffer); 
		} 
 
		pBuffer->Release(); 
		pBuffer = NULL; 
 
		if (bCanContinue && m_pPendingBufferList && m_pPendingBufferList->GetCount() > 0) 
		{ 
			pBuffer = (IHXBuffer*) m_pPendingBufferList->RemoveHead(); 
		} 
	} // while 
 
	if (m_bPlaying) 
	{ 
		UINT32	ulCurrentTime = HX_GET_TICKCOUNT(); 
		if (CALCULATE_ELAPSED_TICKS(m_ulLastTimeSync, ulCurrentTime) > 100) 
		{ 
			m_ulLastTimeSync = ulCurrentTime; 
			OnTimeSync(); 
		} 
	} 
 
//@ This is an ugly hack, but it seems to work very well. 
// Hopefully I can figure it out and fix it properly at some point. 
// (mclifton 10/5/99) 
/*if (pAudioOutData && m_bPlaying && m_pPendingBufferList && m_pPendingBufferList->GetCount() > 0) 
{ 
snooze(50000); 
_Imp_Write(NULL); 
} 
if (pAudioOutData && m_bPlaying && m_pPendingBufferList && m_pPendingBufferList->GetCount() > 0) 
{ 
snooze(50000); 
_Imp_Write(NULL); 
}*/ 
 
	return theErr; 
} 
 
HX_RESULT  
CAudioOutBeOS::_Imp_Reset() 
{ 
    m_ulCurrentPlayTime = 0; 
    m_bFirstWrite = TRUE; 
 
	m_ulNextWriteOffset = 0; 
 
	if (m_player) 
		m_player->StopPlaying(); 
	m_bPlaying = FALSE; 
 
    return HXR_OK; 
} 
 
HX_RESULT  
CAudioOutBeOS::_Imp_Drain() 
{ 
    return HXR_OK; 
} 
 
BOOL  
CAudioOutBeOS::_Imp_SupportsVolume() 
{ 
    return TRUE; 
} 
 
UINT16  
CAudioOutBeOS::_Imp_GetVolume() 
{ 
	float	vol = 0.0f; 
 
	if (m_player) 
		vol = m_player->Gain(); 
	return (UINT16)(vol * 100.0f); 
} 
 
HX_RESULT  
CAudioOutBeOS::_Imp_SetVolume(const UINT16 uVolume) 
{ 
	float	vol = (float)uVolume / 100.0f; 
 
	if (m_player) 
		m_player->SetGain(vol); 
    return HXR_OK; 
} 
 
HX_RESULT  
CAudioOutBeOS::_Imp_CheckFormat(const HXAudioFormat* pFormat) 
{ 
    return HXR_OK; 
} 
 
HX_RESULT  
CAudioOutBeOS::_Imp_GetCurrentTime(ULONG32& ulCurrentTime) 
{ 
	size_t	curPos = 0; 
 
	if (m_player) 
	{ 
		if (m_player->IsPlaying()) 
			curPos = m_player->CurrentPosition() * m_ulFrameSize; 
 
		// This method of calculating elapsed time was basically copied over from 
		// the DirectSound code. The bad news is that it is error-prone. Converting 
		// from bytes to milliseconds is prone to roundoff, leading to an accumulation 
		// of error, causing a drift of synchronization between audio and video. 
		// (That DirectSound code has caused me nothing but headaches...) 
		//m_ulCurrentPlayTime += 
		//	CalcMs(CalculateElapsedBytes(m_ulLastPlayCursor, curPos)); 
 
		// The more accurate way to do it is to accumulate elapsed bytes, then 
		// convert the total to milliseconds. The elapsed bytes is accurate to 
		// within a video frame or so. Even though it can't be absolutely accurate, 
		// at least it won't drift over time. 
		m_ulCurrentPlayTime += 
			CalculateElapsedBytes(m_ulLastPlayCursor, curPos); 
 
		m_ulLastPlayCursor = curPos; 
	} 
	// old method 
	//ulCurrentTime = m_ulCurrentPlayTime; 
	// new method 
	ulCurrentTime = CalcMs(m_ulCurrentPlayTime); 
 
	return HXR_OK; 
} 
 
 
UINT16 
CAudioOutBeOS::_NumberOfBlocksRemainingToPlay(void) 
{ 
	UINT32	res = 0; 
	size_t	curPos = 0; 
 
	// add up the number of audio bytes queued up 
	if (m_pPendingBufferList) 
	{ 
		LISTPOSITION i = m_pPendingBufferList->GetHeadPosition(); 
 
		while (i) 
		{ 
			res += ((IHXBuffer *)m_pPendingBufferList->GetAt(i))->GetSize(); 
			m_pPendingBufferList->GetNext(i); 
		} 
	} 
 
	// add in the bytes that are currently in the playback buffer 
	if (m_player) 
	{ 
		UINT32	playingBytes = 0; 
 
		if (m_player->IsPlaying()) 
			curPos = m_player->CurrentPosition() * m_ulFrameSize; 
 
		if (curPos < m_ulNextWriteOffset) 
			playingBytes += m_ulNextWriteOffset - curPos; 
		else 
			playingBytes += (m_ulBufferSize - curPos) + m_ulNextWriteOffset; 
 
		res += playingBytes; 
	} 
 
	if (m_bGotAWrite) 
		m_ulOldBytesLeft = res; 
	else if (res > m_ulOldBytesLeft) 
	{ 
		fprintf(stderr, "Buffer overflow!\n"); 
		// This is a bad situation - I wish this never happened. 
		// But what should I do when it does happen? 
		// I used to return 0, since I thought that would force more 
		// audio buffers my way, but it also seems to introduce more glitches. 
		//res = 0; 
	} 
	m_bGotAWrite = FALSE; 
 
	res /= m_ulBlockSize; 
 
	return res; 
} 
 
void 
CAudioOutBeOS::SetFormat(const HXAudioFormat* pFormat) 
{ 
    ::memset(&m_gameSoundFormat, 0, sizeof(gs_audio_format)); 
 
	m_gameSoundFormat.frame_rate = pFormat->ulSamplesPerSec; 
	m_gameSoundFormat.channel_count = pFormat->uChannels; 
	switch (pFormat->uBitsPerSample) 
	{ 
		case 8: 
			m_gameSoundFormat.format = gs_audio_format::B_GS_U8; 
			break; 
		case 16: 
			m_gameSoundFormat.format = gs_audio_format::B_GS_S16; 
			break; 
	} 
	m_gameSoundFormat.byte_order = B_MEDIA_LITTLE_ENDIAN; 
	m_gameSoundFormat.buffer_size = 2048; 
 
	m_ulFrameSize = m_gameSoundFormat.channel_count * ((m_gameSoundFormat.format==gs_audio_format::B_GS_U8)?1:2); 
} 
 
inline UINT32  
CAudioOutBeOS::CalcMs(ULONG32 ulNumBytes) 
{ 
    return ( (ULONG32) (( 1000.0 
		/ (m_ulFrameSize 
		*  m_gameSoundFormat.frame_rate) ) 
		*  ulNumBytes) ); 
} 
 
 
inline UINT32 
CAudioOutBeOS::CalculateElapsedBytes(UINT32 ulLastBytePos, UINT32 ulCurrentBytePos) 
{ 
	 return ((ulCurrentBytePos >= ulLastBytePos) ? (ulCurrentBytePos - ulLastBytePos) : (ulCurrentBytePos + (m_ulBufferSize - ulLastBytePos))); 
} 
 
#if _BEOS_AUDIODEV_CALLBACK 
void 
CAudioOutBeOS::DoTimeSyncs(void) 
{ 
	ReschedPlaybackCheck(); 
	OnTimeSync(); 
 
	return; 
} 
 
HX_RESULT 
CAudioOutBeOS::ReschedPlaybackCheck() 
{ 
	HX_RESULT theErr = HXR_OK; 
 
	if (m_bCallbackPending) 
		return theErr; 
 
	*m_pPlaybackCountCBTime += (int)(m_ulGranularity * 1000) / 2; 
	// Put this back in the scheduler. 
	HXPlaybackCountCb	*pCallback = 0; 
	pCallback = new HXPlaybackCountCb(TRUE); 
	if (pCallback) 
	{ 
		pCallback->m_pAudioDeviceObject = this; 
		m_bCallbackPending = TRUE; 
		m_PendingCallbackID = m_pScheduler->AbsoluteEnter(pCallback, *((HXTimeval *)m_pPlaybackCountCBTime)); 
	} 
	else 
		theErr = HXR_OUTOFMEMORY;  // but ignored, why? 
 
	return theErr; 
} 
 
CAudioOutBeOS::HXPlaybackCountCb::HXPlaybackCountCb(BOOL timed)  
	: m_lRefCount(0) 
	, m_pAudioDeviceObject(0) 
	, m_timed(timed) 
{ 
} 
 
CAudioOutBeOS::HXPlaybackCountCb::~HXPlaybackCountCb() 
{ 
} 
 
/* 
 * IUnknown methods 
 */ 
///////////////////////////////////////////////////////////////////////// 
//      Method: 
//              IUnknown::QueryInterface 
//      Purpose: 
//              Implement this to export the interfaces supported by your  
//              object. 
// 
STDMETHODIMP CAudioOutBeOS::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) CAudioOutBeOS::HXPlaybackCountCb::AddRef() 
{ 
	return InterlockedIncrement(&m_lRefCount); 
} 
 
///////////////////////////////////////////////////////////////////////// 
//      Method: 
//              IUnknown::Release 
//      Purpose: 
//              Everyone usually implements this the same... feel free to use 
//              this implementation. 
// 
STDMETHODIMP_(ULONG32) CAudioOutBeOS::HXPlaybackCountCb::Release() 
{ 
	if (InterlockedDecrement(&m_lRefCount) > 0) 
	{ 
		return m_lRefCount; 
	} 
 
	delete this; 
	return 0; 
} 
 
/* 
 *      IHXPlaybackCountCb methods 
 */ 
STDMETHODIMP CAudioOutBeOS::HXPlaybackCountCb::Func(void) 
{ 
	if (m_pAudioDeviceObject) 
	{ 
		if (!m_timed) 
		{ 
			m_pAudioDeviceObject->_Imp_Write(NULL); 
		} 
		else 
		{ 
			m_pAudioDeviceObject->_Imp_Write(NULL); 
			m_pAudioDeviceObject->m_bCallbackPending = FALSE; 
			m_pAudioDeviceObject->DoTimeSyncs(); 
		} 
	} 
 
	return HXR_OK; 
} 
#endif