|
|
/*==========================================================================
* * Copyright (C) 1999 Microsoft Corporation. All Rights Reserved. * * File: dvsndt.cpp * Content: Implementation of CSoundTarget class * * History: * Date By Reason * ==== == ====== * 09/02/99 rodtoll Created * 09/08/99 rodtoll Updated to handle lockup of playback buffer * rodtoll Added handling for restarting playback buffer or * handling slowdown/speedup of buffer playback * because of high cpu loads. * rodtoll Added write-ahead of silence to the buffers to that * in high CPU conditions silence will be played instead * of old voice. * 09/14/99 rodtoll Added new WriteAheadSilence which writes silence ahead * of the current write location to prevent high CPU from * playing old data. * 09/20/99 rodtoll Added checks for memory allocation failures * rodtoll Added handlers for buffer loss * 10/05/99 rodtoll Added additional comments * 10/25/99 rodtoll Fix: Bug #114223 - Debug messages being printed at error level when inappropriate * 11/02/99 pnewson Fix: Bug #116365 - using wrong DSBUFFERDESC * 11/12/99 rodtoll Updated to use new abstractions for playback (allows use * of waveOut with this class). * 11/13/99 rodtoll Re-activated code which pushes write pointer ahead if * buffer pointer passes us. * 01/24/2000 rodtoll Fix: Bug #129427 - Destroying transport before calling Delete3DSound * 01/27/2000 rodtoll Bug #129934 - Update SoundTargets to take DSBUFFERDESC * 02/17/2000 rodtoll Bug #133691 - Choppy audio - queue was not adapting * Added instrumentation * 04/14/2000 rodtoll Bug #32215 - Voice Conference Lost after resume from hibernation * Updated code to use new restore handling in dsound layer * 05/17/2000 rodtoll Bug #35110 Simultaneous playback of 2 voices results in distorted playback * 06/21/2000 rodtoll Fix: Bug #35767 - Must implement ability for dsound effects on voice buffers * Added new constructor/init that takes pre-built buffers * 07/09/2000 rodtoll Added signature bytes * 07/28/2000 rodtoll Bug #40665 - DirectSound reports 1 buffer leaked * 11/16/2000 rodtoll Bug #47783 - DPVOICE: Improve debugging of failures caused by DirectSound errors. * 04/02/2001 simonpow Bug #354859 Fixes for PREfast (initialising local variables in RestoreLostBuffer) * 04/21/2001 rodtoll MANBUG #50058 DPVOICE: VoicePosition: No sound for couple of seconds when position bars are moved * - Added initialization for uninitialized variables * - Removed ifdef'ed out code. * ***************************************************************************/
#include "dxvoicepch.h"
#define SOUNDTARGET_WRITEAHEAD 2
// Max # of restarts to attempt on a buffer
#define SOUNDTARGET_MAX_RESTARTS 10
// Max # of frames of silence which are written ahead of the latest frame
// of audio
#define SOUNDTARGET_MAX_WRITEAHEAD 3
#undef DPF_MODNAME
#define DPF_MODNAME "CSoundTarget::CSoundTarget"
//
// Constructor
//
// This constructor is used when a DIRECTSOUND buffer needs to be created. If there is already
// a DIRECTSOUNDBUFFER you wish to attach the sound target object to, use the other constructor
// type.
//
CSoundTarget::CSoundTarget( DVID dvidTarget, CAudioPlaybackDevice *lpPlaybackDevice, LPDSBUFFERDESC lpdsBufferDesc, DWORD dwPriority, DWORD dwFlags, DWORD dwFrameSize ): m_lpds3dBuffer(NULL), m_lpAudioPlaybackBuffer(NULL), m_lpMixBuffer(NULL), m_dwSignature(VSIG_SOUNDTARGET), m_lRefCount(1) { CAudioPlaybackBuffer *lpdsBuffer; LPVOID lpvBuffer1, lpvBuffer2; DWORD dwBufferSize1, dwBufferSize2;
Stats_Init();
m_hrInitResult = lpPlaybackDevice->CreateBuffer( lpdsBufferDesc, dwFrameSize, &lpdsBuffer );
if( FAILED( m_hrInitResult ) ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Could not create the sound buffer hr=0x%x", m_hrInitResult ); return; }
m_hrInitResult = lpdsBuffer->Lock( 0, 0, &lpvBuffer1, &dwBufferSize1, &lpvBuffer2, &dwBufferSize2, DSBLOCK_ENTIREBUFFER );
if( FAILED( m_hrInitResult ) ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Could not lock the sound buffer hr=0x%x", m_hrInitResult ); m_hrInitResult = DVERR_LOCKEDBUFFER; return; }
if( lpdsBufferDesc->lpwfxFormat->wBitsPerSample == 8 ) { memset( lpvBuffer1, 0x80, dwBufferSize1 ); } else { memset( lpvBuffer1, 0x00, dwBufferSize1 ); }
m_hrInitResult = lpdsBuffer->UnLock( lpvBuffer1, dwBufferSize1, lpvBuffer2, dwBufferSize2 );
if( FAILED( m_hrInitResult ) ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Could not unlock the sound buffer hr=0x%x", m_hrInitResult ); return; }
// Required always
dwFlags |= DSBPLAY_LOOPING;
m_hrInitResult = lpdsBuffer->Play( dwPriority, dwFlags );
if( FAILED( m_hrInitResult ) ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Could not play the sound buffer hr=0x%x", m_hrInitResult ); return; }
m_hrInitResult = Initialize( dvidTarget, lpdsBuffer, (lpdsBufferDesc->lpwfxFormat->wBitsPerSample==8), dwPriority, dwFlags, dwFrameSize ); }
#undef DPF_MODNAME
#define DPF_MODNAME "CSoundTarget::RestoreLostBuffer"
//
// RestoreLostBuffer
//
// Handles restoration of lost directsound buffers.
//
HRESULT CSoundTarget::RestoreLostBuffer() { HRESULT hr = DSERR_BUFFERLOST;
DPFX(DPFPREP, 0, "Restoring lost buffer" );
while( hr == DSERR_BUFFERLOST ) { hr = m_lpAudioPlaybackBuffer->Restore();
DPFX(DPFPREP, 0, "Buffer result for restore was 0x%x", hr );
if( hr == DS_OK ) { hr = m_lpAudioPlaybackBuffer->GetCurrentPosition( &m_dwWritePos ); DPFX(DPFPREP, 0, "GetCurrentPos returned 0x%x", hr );
if( hr != DSERR_BUFFERLOST && FAILED( hr ) ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Lost buffer while getting pos, failed to get pos hr=0x%x", hr ); return hr; } } else if( hr != DSERR_BUFFERLOST ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Error restoring buffer hr=0x%x", hr ); return hr; } }
// STATBLOCK: Begin
m_statPlay.m_dwNumBL++; // STATBLOCK: End
m_dwNextWritePos = m_dwWritePos + (m_dwFrameSize*SOUNDTARGET_WRITEAHEAD); m_dwNextWritePos %= m_dwBufferSize;
m_dwLastWritePos = m_dwWritePos;
return hr; }
#undef DPF_MODNAME
#define DPF_MODNAME "CSoundTarget::CSoundTarget"
//
// Constructor
//
// This constructor is used when you wish to attach a soundtarget to an existing
// DirectSond buffer.
//
CSoundTarget::CSoundTarget( DVID dvidTarget, CAudioPlaybackDevice *lpads, CAudioPlaybackBuffer *lpdsBuffer, LPDSBUFFERDESC lpdsBufferDesc, DWORD dwPriority, DWORD dwFlags, DWORD dwFrameSize ): m_lpds3dBuffer(NULL), m_lpAudioPlaybackBuffer(NULL), m_lpMixBuffer(NULL), m_dwSignature(VSIG_SOUNDTARGET), m_lRefCount(1) { m_hrInitResult = Initialize( dvidTarget, lpdsBuffer, (lpdsBufferDesc->lpwfxFormat->wBitsPerSample==8), dwPriority, dwFlags, dwFrameSize ); }
#undef DPF_MODNAME
#define DPF_MODNAME "CSoundTarget::CSoundTarget"
CSoundTarget::CSoundTarget( DVID dvidTarget, CAudioPlaybackDevice *lpads, LPDIRECTSOUNDBUFFER lpdsBuffer, BOOL fEightBit, DWORD dwPriority, DWORD dwFlags, DWORD dwFrameSize ): m_lpds3dBuffer(NULL), m_lpAudioPlaybackBuffer(NULL), m_lpMixBuffer(NULL), m_dwSignature(VSIG_SOUNDTARGET), m_lRefCount(1) { CDirectSoundPlaybackBuffer *pAudioBuffer = NULL;
// Create audio buffer to wrap buffer for call to initialize
pAudioBuffer = new CDirectSoundPlaybackBuffer( lpdsBuffer );
if( pAudioBuffer == NULL ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Error allocating memory" ); m_hrInitResult = DVERR_OUTOFMEMORY; return; }
// Required always
dwFlags |= DSBPLAY_LOOPING;
m_hrInitResult = pAudioBuffer->Play( dwPriority, dwFlags );
if( FAILED( m_hrInitResult ) ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed playing new buffer hr=0x%x", m_hrInitResult ); return; }
m_hrInitResult = Initialize( dvidTarget, pAudioBuffer, fEightBit, dwPriority, dwFlags, dwFrameSize ); }
#undef DPF_MODNAME
#define DPF_MODNAME "CSoundTarget::~CSoundTarget"
//
// Destructor
//
// Destroys the 3d and buffer pointers and frees memory.
//
CSoundTarget::~CSoundTarget() { Stats_End();
if( m_lpds3dBuffer != NULL ) { m_lpds3dBuffer->Release(); m_lpds3dBuffer = NULL; }
if( m_lpAudioPlaybackBuffer != NULL ) { m_lpAudioPlaybackBuffer->Stop(); delete m_lpAudioPlaybackBuffer; m_lpAudioPlaybackBuffer = NULL; }
if( m_lpMixBuffer != NULL ) { delete [] m_lpMixBuffer; m_lpMixBuffer = NULL; }
if( SUCCEEDED( m_hrInitResult ) ) { DNDeleteCriticalSection( &m_csGuard ); }
m_dwSignature = VSIG_SOUNDTARGET_FREE; }
#undef DPF_MODNAME
#define DPF_MODNAME "CSoundTarget::AdjustWritePtr"
//
// AdjustWritePtr
//
// This function ensures that the directsoundbuffer is acting as it should. It handles
// checking the write pointer and determining if some form of error or problem has
// occured and taking the appropriate corrective action.
//
// E.g. the buffer hasn't moved since the last frame, the buffer hasn't moved enough,
// the buffer has skipped ahead etc.
//
// This function should be called once per mixing pass.
//
// Call before writing the frame
//
HRESULT CSoundTarget::AdjustWritePtr() { HRESULT hr; LONG lDifference, lHalfSize; DWORD dwCurrentTick;
hr = m_lpAudioPlaybackBuffer->GetCurrentPosition( &m_dwWritePos );
if( FAILED( hr ) ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "GetCurrentPosition Failed hr=0x%x", hr ); DSASSERT( FALSE ); return hr; }
// STATSBLOCK: Begin
m_statPlay.m_dwNumRuns++; // STATSBLOCK: End
DWORD dwTmpAdvance, dwTmpLead;
dwCurrentTick = GetTickCount();
if( m_dwLastWritePos > m_dwWritePos ) { dwTmpAdvance = (m_dwBufferSize - m_dwLastWritePos) + m_dwWritePos; } else { dwTmpAdvance = m_dwWritePos - m_dwLastWritePos; }
if( m_dwNextWritePos < m_dwWritePos ) { dwTmpLead = (m_dwBufferSize-m_dwWritePos) + m_dwNextWritePos; } else { dwTmpLead = m_dwNextWritePos - m_dwWritePos; }
DPFX(DPFPREP, PWI_DEBUGOUTPUT_LEVEL, "PWI, [0x%x], %d, %d, %d, %d, %d, %d", m_dvidTarget, m_dwWritePos, dwTmpAdvance, dwCurrentTick - m_dwLastWriteTime, m_dwNextWritePos, dwTmpLead, m_dwFrameSize );
// STATSBLOCK: Begin
DWORD dwTmpDiff = dwCurrentTick - m_dwLastWriteTime;
if( dwTmpDiff > m_statPlay.m_dwPMMSMax ) { m_statPlay.m_dwPMMSMax = dwTmpDiff; }
if( dwTmpDiff < m_statPlay.m_dwPMMSMin ) { m_statPlay.m_dwPMMSMin = dwTmpDiff; }
m_statPlay.m_dwPMMSTotal += dwTmpDiff;
if( dwTmpAdvance > m_statPlay.m_dwPMBMax ) { m_statPlay.m_dwPMBMax = dwTmpAdvance; }
if( dwTmpAdvance < m_statPlay.m_dwPMBMin ) { m_statPlay.m_dwPMBMin = dwTmpAdvance; }
m_statPlay.m_dwPMBTotal += dwTmpAdvance;
if( dwTmpLead > m_statPlay.m_dwPLMax ) { m_statPlay.m_dwPLMax = dwTmpLead; }
if( dwTmpLead < m_statPlay.m_dwPLMin ) { m_statPlay.m_dwPLMin = dwTmpLead; }
m_statPlay.m_dwPLTotal += dwTmpLead; // STATSBLOCK: End
lHalfSize = m_dwBufferSize / 2; lDifference = m_dwNextWritePos - m_dwWritePos;
// If the write position is somehow ahead of position AND
//
if( lDifference < 0 && lDifference > (-1*lHalfSize) ) { m_dwNextWritePos = m_dwWritePos; m_dwNextWritePos += (m_dwFrameSize * SOUNDTARGET_WRITEAHEAD); m_dwNextWritePos %= m_dwBufferSize; DPFX(DPFPREP, PWI_DEBUGOUTPUT_LEVEL, "PWI, [0x%x], Punt --> %d", m_dvidTarget, m_dwNextWritePos );
// STATSBLOCK: Begin
m_statPlay.m_dwPPunts++;
DPFX(DPFPREP, DVF_GLITCH_DEBUG_LEVEL, "GLITCH: [0x%x] Playback: Write pointer has fallen behind buffer pointer. Compensating", m_dvidTarget );
m_statPlay.m_dwGlitches++;
// STATSBLOCK: End
}
m_dwLastWritePos = m_dwWritePos; m_dwLastWriteTime = dwCurrentTick;
if( m_dwNextWritePos < m_dwWritePos && (m_dwNextWritePos + m_dwFrameSize) > m_dwWritePos ) { DPFX(DPFPREP, PWI_DEBUGOUTPUT_LEVEL, "PWI, [0x%x], Ignore - Crossing", m_dvidTarget ); m_fIgnoreFrame = TRUE; // STATSBLOCK: Begin
m_statPlay.m_dwPIgnore++;
DPFX(DPFPREP, DVF_GLITCH_DEBUG_LEVEL, "GLITCH: [0x%x] Playback: Current frame will cross buffer pointer. Ignoring", m_dvidTarget ); DPFX(DPFPREP, DVF_GLITCH_DEBUG_LEVEL, "GLITCH: [0x%x] Playback: May be catching up with buiffer pointer", m_dvidTarget );
m_statPlay.m_dwGlitches++; // STATSBLOCK: End
} else { m_fIgnoreFrame = FALSE; }
return DV_OK; }
#undef DPF_MODNAME
#define DPF_MODNAME "CSoundTarget::WriteAheadSilence"
//
// WriteAheadSilence
//
// This function is responsible for writing frames of silence ahead of the latest frame
// placed in the buffer. This way if because of high CPU writer thread donesn't get woken
// up silence will be played instead of old speech.
//
// Called AFTER latest frame of data has been written.
//
HRESULT CSoundTarget::WriteAheadSilence() { HRESULT hr; DWORD dwBufferSize1, dwBufferSize2; LPVOID lpvBuffer1, lpvBuffer2;
if( m_dwNextWritePos < m_dwWritePos && (m_dwNextWritePos + (m_dwFrameSize*SOUNDTARGET_MAX_WRITEAHEAD)) > m_dwWritePos ) { DPFX(DPFPREP, PWI_DEBUGOUTPUT_LEVEL, "PWI, [0x%x], Ignore2 - Crossing", m_dvidTarget );
// STATSBLOCK: Begin
m_statPlay.m_dwSIgnore++;
DPFX(DPFPREP, DVF_GLITCH_DEBUG_LEVEL, "GLITCH: Playback: Silence will cross buffer pointer. Ignoring" ); DPFX(DPFPREP, DVF_GLITCH_DEBUG_LEVEL, "GLITCH: Playback: May be catching up with buiffer pointer" );
// STATSBLOCK: End
return DV_OK; }
hr = m_lpAudioPlaybackBuffer->Lock( m_dwNextWritePos, m_dwFrameSize*SOUNDTARGET_MAX_WRITEAHEAD, &lpvBuffer1, &dwBufferSize1, &lpvBuffer2, &dwBufferSize2, 0 );
if( FAILED( hr ) ) { DSASSERT( FALSE ); DPFX(DPFPREP, DVF_ERRORLEVEL, "Lock() Failed hr=0x%x", hr ); return hr; }
memset( lpvBuffer1, (m_fEightBit) ? 0x80 : 0x00, dwBufferSize1 ); memset( lpvBuffer2, (m_fEightBit) ? 0x80 : 0x00, dwBufferSize2 );
hr = m_lpAudioPlaybackBuffer->UnLock( lpvBuffer1, dwBufferSize1, lpvBuffer2, dwBufferSize2 );
if( FAILED( hr ) ) { DSASSERT( FALSE ); DPFX(DPFPREP, DVF_ERRORLEVEL, "Unlock() Failed hr=0x%x", hr ); return hr; }
return DV_OK; }
#undef DPF_MODNAME
#define DPF_MODNAME "CSoundTarget::MixInSingle"
//
// MixInSingle
//
// This function is an optimization.
//
// If there is only one frame to mix into this buffer, this function performs the
// MixIn and Commit all in one step. You still must call Commit before the next
// frame hwoever.
//
HRESULT CSoundTarget::MixInSingle( LPBYTE lpbBuffer ) { HRESULT hr;
DWORD dwBufferSize1, dwBufferSize2; LPVOID lpvBuffer1, lpvBuffer2;
hr = AdjustWritePtr(); if( FAILED( hr ) ) { DSASSERT( FALSE ); DPFX(DPFPREP, DVF_ERRORLEVEL, "AdjustWritePtr Failed hr=0x%x", hr ); return hr; }
if( !m_fIgnoreFrame ) { hr = m_lpAudioPlaybackBuffer->Lock( m_dwNextWritePos, m_dwFrameSize, &lpvBuffer1, &dwBufferSize1, &lpvBuffer2, &dwBufferSize2, 0 );
if( FAILED( hr ) ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Lock() Failed hr=0x%x", hr ); return hr; }
memcpy( lpvBuffer1, lpbBuffer, dwBufferSize1 );
if( dwBufferSize2 ) { memcpy( lpvBuffer2, &lpbBuffer[dwBufferSize1], dwBufferSize2 ); }
hr = m_lpAudioPlaybackBuffer->UnLock( lpvBuffer1, dwBufferSize1, lpvBuffer2, dwBufferSize2 );
if( FAILED( hr ) ) { DSASSERT( FALSE ); DPFX(DPFPREP, DVF_ERRORLEVEL, "UnLock() Failed hr=0x%x", hr ); return hr; } }
m_dwNextWritePos += m_dwFrameSize; m_dwNextWritePos %= m_dwBufferSize;
m_bCommited = TRUE; m_fMixed = TRUE;
return DV_OK; }
#undef DPF_MODNAME
#define DPF_MODNAME "CSoundTarget::MixIn"
// Mix in a user's audio.
//
// Simply copies and promotes the audio samples into the buffer with LONG's
// in it.
//
HRESULT CSoundTarget::MixIn( const BYTE* lpbBuffer ) { DWORD dwIndex;
if( !m_fMixed ) { FillBufferWithSilence( m_lpMixBuffer, m_fEightBit, m_dwFrameSize ); m_fMixed = TRUE; }
if( !m_fIgnoreFrame ) MixInBuffer( m_lpMixBuffer, lpbBuffer, m_fEightBit, m_dwFrameSize );
m_bCommited = FALSE; m_fMixed = TRUE;
return DV_OK; }
#undef DPF_MODNAME
#define DPF_MODNAME "CSoundTarget::Commit"
// Commit
//
// If we didn't do a single direct mix, commit the mixed audio to the buffer
//
HRESULT CSoundTarget::Commit() { DWORD dwBufferSize1, dwBufferSize2; LPVOID lpvBuffer1, lpvBuffer2; DWORD dwIndex = 0; HRESULT hr;
if( !m_fMixed ) { FillBufferWithSilence( m_lpMixBuffer, m_fEightBit, m_dwFrameSize );
// STATSBLOCK: Begin
m_statPlay.m_dwNumSilentMixed++; // STATSBLOCK: End
} else { if( !m_fIgnoreFrame ) { // STATSBLOCK: Begin
m_statPlay.m_dwNumMixed++; // STATSBLOCK: End
} }
if( !m_bCommited || !m_fMixed ) { hr = AdjustWritePtr();
if( FAILED( hr ) ) { DSASSERT( FALSE ); DPFX(DPFPREP, DVF_ERRORLEVEL, "AdjustWritePtr() Failed hr=0x%x", hr ); return hr; }
if( !m_fIgnoreFrame ) { hr = m_lpAudioPlaybackBuffer->Lock( m_dwNextWritePos, m_dwFrameSize, &lpvBuffer1, &dwBufferSize1, &lpvBuffer2, &dwBufferSize2, 0 );
if( FAILED( hr ) ) { DSASSERT( FALSE ); DPFX(DPFPREP, DVF_ERRORLEVEL, "Lock() Failed hr=0x%x", hr ); return hr; }
// Mix first half
NormalizeBuffer( (BYTE *) lpvBuffer1, m_lpMixBuffer, m_fEightBit, dwBufferSize1 );
if( dwBufferSize2 > 0 ) { if( m_fEightBit ) { // Mix second half
NormalizeBuffer( (BYTE *) lpvBuffer2, &m_lpMixBuffer[dwBufferSize1], m_fEightBit, dwBufferSize2 ); } else { // Mix second half
NormalizeBuffer( (BYTE *) lpvBuffer2, &m_lpMixBuffer[(dwBufferSize1 >> 1)], m_fEightBit, dwBufferSize2 ); } }
hr = m_lpAudioPlaybackBuffer->UnLock( lpvBuffer1, dwBufferSize1, lpvBuffer2, dwBufferSize2 );
if( FAILED( hr ) ) { DSASSERT( FALSE ); DPFX(DPFPREP, DVF_ERRORLEVEL, "UnLock Failed hr=0x%x", hr ); return hr; } }
m_dwNextWritePos += m_dwFrameSize; m_dwNextWritePos %= m_dwBufferSize;
m_bCommited = FALSE; m_fMixed = FALSE;
return DV_OK; }
hr = WriteAheadSilence();
if( FAILED( hr ) ) { DSASSERT( FALSE ); DPFX(DPFPREP, 0, "WriteAhead Failed hr=0x%x", hr ); }
m_fMixed = FALSE; m_bCommited = FALSE;
return DV_OK; }
#undef DPF_MODNAME
#define DPF_MODNAME "CSoundTarget::Initialize"
//
// Initialize
//
// NOTE: Takes a reference to the buffer
//
// Attaches a sound target to the specified sound buffer, initializes the object and creates
// the associated 3d buffer.
//
HRESULT CSoundTarget::Initialize( DVID dvidTarget, CAudioPlaybackBuffer *lpdsBuffer, BOOL fEightBit, DWORD dwPriority, DWORD dwFlags, DWORD dwFrameSize ) { HRESULT hr = DV_OK; PVOID pvBuffer1 = NULL, pvBuffer2 = NULL; DWORD dwBufferSize1, dwBufferSize2;
// Determine the buffer size (in bytes)
hr = lpdsBuffer->Lock( 0, 0, &pvBuffer1, &dwBufferSize1, &pvBuffer2, &dwBufferSize2, DSBLOCK_ENTIREBUFFER );
if( FAILED( hr ) ) { DSASSERT( FALSE ); DPFX(DPFPREP, DVF_ERRORLEVEL, "Lock() failed hr=0x%x", hr ); return hr; }
hr = lpdsBuffer->UnLock( pvBuffer1, dwBufferSize1, pvBuffer2, dwBufferSize2 );
if( FAILED( hr ) ) { DSASSERT( FALSE ); DPFX(DPFPREP, DVF_ERRORLEVEL, "UnLock() failed hr=0x%x", hr ); return hr; }
m_lpds3dBuffer = NULL; m_lpAudioPlaybackBuffer = NULL; m_lpMixBuffer = NULL;
// We always need the looping flag
dwFlags |= DSBPLAY_LOOPING;
m_dwPlayFlags = dwFlags; m_dwPriority = dwPriority; m_fIgnoreFrame = FALSE; m_dwLastWriteTime = GetTickCount(); m_lpstNext = NULL; m_dwNumResets = 0; m_dwNumSinceMove = 1; m_bCommited = FALSE;
m_dvidTarget = dvidTarget; m_dwFrameSize = dwFrameSize; m_dwBufferSize = dwBufferSize1;
// STATSBLOCK: Begin
Stats_Init(); // STATSBLOCK: End
m_fLastFramePushed = FALSE;
m_lpAudioPlaybackBuffer = lpdsBuffer;
hr = m_lpAudioPlaybackBuffer->Get3DBuffer(&m_lpds3dBuffer); if( FAILED( hr ) ) { DSASSERT( FALSE ); DPFX(DPFPREP, DVF_ERRORLEVEL, "QueryInterface failed hr=0x%x", hr ); }
m_dwNextWritePos = 0;
if( fEightBit ) { m_fEightBit = TRUE; m_dwMixSize = dwFrameSize; } else { m_fEightBit = FALSE; m_dwMixSize = dwFrameSize / 2; }
m_lpMixBuffer = new LONG[m_dwMixSize];
if( m_lpMixBuffer == NULL ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Out of memory" ); m_lpds3dBuffer->Release(); m_lpds3dBuffer = NULL; return DVERR_OUTOFMEMORY; }
if (!DNInitializeCriticalSection( &m_csGuard )) { return DVERR_OUTOFMEMORY; }
m_fMixed = FALSE;
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CSoundTarget::AddRef"
LONG CSoundTarget::AddRef() { LONG lNewCount; DNEnterCriticalSection( &m_csGuard );
DPFX(DPFPREP, DVF_SOUNDTARGET_DEBUG_LEVEL, "SOUNDTARGET: [0x%x] lRefCount %d --> %d", m_dvidTarget, m_lRefCount, m_lRefCount+1 ); m_lRefCount++;
lNewCount = m_lRefCount;
DNLeaveCriticalSection( &m_csGuard );
return lNewCount; }
#undef DPF_MODNAME
#define DPF_MODNAME "CSoundTarget::Release"
LONG CSoundTarget::Release() { LONG lNewCount;
DNEnterCriticalSection( &m_csGuard );
DNASSERT( m_lRefCount > 0 );
DPFX(DPFPREP, DVF_SOUNDTARGET_DEBUG_LEVEL, "SOUNDTARGET: [0x%x] lRefCount %d --> %d", m_dvidTarget, m_lRefCount, m_lRefCount-1 ); m_lRefCount--;
lNewCount = m_lRefCount;
DNLeaveCriticalSection( &m_csGuard );
// Reference reaches 0, destroy!
if( lNewCount == 0 ) { DPFX(DPFPREP, DVF_SOUNDTARGET_DEBUG_LEVEL, "SOUNDTARGET: [0x%x] DESTROYING", m_dvidTarget, m_lRefCount, m_lRefCount+1 ); delete this; }
return lNewCount; }
#undef DPF_MODNAME
#define DPF_MODNAME "CSoundTarget::StartMix"
//
// StartMix
//
// Called ONCE only.
//
// Call this function right before you wish to perform the first mix on the buffer.
// Initializes the object to match the current state of the associated directsound
// buffer
//
HRESULT CSoundTarget::StartMix() { HRESULT hr;
hr = m_lpAudioPlaybackBuffer->GetCurrentPosition( &m_dwWritePos );
if( FAILED( hr ) ) { DSASSERT( FALSE ); DPFX(DPFPREP, DVF_ERRORLEVEL, "GetCurrentPosition Failed hr=0x%x", hr ); return hr; }
m_dwNextWritePos = m_dwWritePos + (m_dwFrameSize*SOUNDTARGET_WRITEAHEAD); m_dwNextWritePos %= m_dwBufferSize;
m_dwLastWritePos = m_dwWritePos;
// STATBLOCK: Begin
Stats_Begin(); // STATBLOCK: End
return DV_OK; }
void CSoundTarget::GetStats( PlaybackStats *statPlayback ) { memcpy( statPlayback, &m_statPlay, sizeof( PlaybackStats ) ); }
void CSoundTarget::Stats_Init() { memset( &m_statPlay, 0x00, sizeof( PlaybackStats ) );
m_statPlay.m_dwFrameSize = m_dwFrameSize; m_statPlay.m_dwBufferSize = m_dwBufferSize;
m_statPlay.m_dwPMMSMin = 0xFFFFFFFF; m_statPlay.m_dwPMBMin = 0xFFFFFFFF; m_statPlay.m_dwPLMin = 0xFFFFFFFF; m_statPlay.m_dwTimeStart = GetTickCount(); }
void CSoundTarget::Stats_Begin() { m_statPlay.m_dwStartLag = GetTickCount() - m_statPlay.m_dwTimeStart; }
void CSoundTarget::Stats_End() { char tmpBuffer[200];
m_statPlay.m_dwTimeStop = GetTickCount();
DWORD dwPlayRunLength = m_statPlay.m_dwTimeStop - m_statPlay.m_dwTimeStart;
if( dwPlayRunLength == 0 ) dwPlayRunLength = 1;
DWORD dwNumInternalRuns = m_statPlay.m_dwNumRuns;
if( dwNumInternalRuns == 0 ) dwNumInternalRuns = 1;
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "--- PLAYBACK BUFFER STATISTICS --------------------------------------[End]" ); DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Buffer for ID : 0x%x", m_dvidTarget ); DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Play Run Length (ms) : %u", dwPlayRunLength ); DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Start Lag : %u", m_statPlay.m_dwStartLag );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Speech Size (Uncomp.) : %u", m_statPlay.m_dwFrameSize ); DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Frames / Buffer : %u", m_statPlay.m_dwBufferSize / m_statPlay.m_dwFrameSize); DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "# of wakeups : %u", m_statPlay.m_dwNumRuns );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Ignored Frames (Speech): %u", m_statPlay.m_dwPIgnore ); DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Ignored Frames (Silent): %u", m_statPlay.m_dwSIgnore ); DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Mixed Frames (Speech) : %u", m_statPlay.m_dwNumMixed ); DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Mixed Frames (Silent) : %u", m_statPlay.m_dwNumSilentMixed );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Punted Frames : %u", m_statPlay.m_dwPPunts ); DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Audio Glitches : %u", m_statPlay.m_dwGlitches );
sprintf( tmpBuffer, "Play Movement (ms) : Avg: %u [%u..%u]", m_statPlay.m_dwPMMSTotal / dwNumInternalRuns, m_statPlay.m_dwPMMSMin, m_statPlay.m_dwPMMSMax );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
sprintf( tmpBuffer, "Play Movement (bytes) : Avg: %u [%u..%u]", m_statPlay.m_dwPMBTotal / dwNumInternalRuns, m_statPlay.m_dwPMBMin, m_statPlay.m_dwPMBMax );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
sprintf( tmpBuffer, "Play Movement (frames) : Avg: %.2f [%.2f..%.2f]", (((float) m_statPlay.m_dwPMBTotal) / ((float) dwNumInternalRuns)) / ((float) m_statPlay.m_dwFrameSize), ((float) m_statPlay.m_dwPMBMin) / ((float) m_statPlay.m_dwFrameSize), ((float) m_statPlay.m_dwPMBMax) / ((float) m_statPlay.m_dwFrameSize) );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer ); sprintf( tmpBuffer, "Play Lead (bytes) : Avg: %u [%u..%u]", m_statPlay.m_dwPLTotal / dwNumInternalRuns, m_statPlay.m_dwPLMin , m_statPlay.m_dwPLMax );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
sprintf( tmpBuffer, "Play Lag (frames) : Avg: %.2f [%.2f..%.2f]", (float) ((float) m_statPlay.m_dwPLTotal / (float) dwNumInternalRuns) / ((float) m_statPlay.m_dwFrameSize), (float) ((float) m_statPlay.m_dwPLMin) / (float) m_statPlay.m_dwFrameSize, (float) ((float) m_statPlay.m_dwPLMax) / (float) m_statPlay.m_dwFrameSize );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "--- PLAYBACK BUFFER STATISTICS ------------------------------------[Begin]" ); }
HRESULT CSoundTarget::GetCurrentLead( PDWORD pdwLead ) { HRESULT hr;
hr = m_lpAudioPlaybackBuffer->GetCurrentPosition( &m_dwWritePos ); if( m_dwNextWritePos < m_dwWritePos ) { *pdwLead = (m_dwBufferSize-m_dwWritePos) + m_dwNextWritePos; } else { *pdwLead = m_dwNextWritePos - m_dwWritePos; }
return DV_OK; }
LPDIRECTSOUND3DBUFFER CSoundTarget::Get3DBuffer() { m_lpDummy = m_lpds3dBuffer; return m_lpds3dBuffer; }
|