|
|
/*==========================================================================
* * Copyright (C) 1999 Microsoft Corporation. All Rights Reserved. * * File: ClientRecordSubSystem.cpp * Content: Recording sub-system. * * History: * Date By Reason * ==== == ====== * 07/19/99 rodtoll Created * 07/22/99 rodtoll Added support for multicast/ client/server sessions * 08/02/99 rodtoll Added new silence detection support. * 08/04/99 rodtoll Added guard to new silence detection so only runs when * appropriate * 08/25/99 rodtoll General Cleanup/Modifications to support new * compression sub-system. * 08/26/99 rodtoll Set msgnum to 0 in constructor. * 08/26/99 rodtoll Added check for record mute flag to fix record muting\ * 08/27/99 rodtoll General cleanup/Simplification of recording subsystem * Added reset of message when target changes * Fixed recording start/stop notifications * Fixed level readings when using voice activation * 08/30/99 rodtoll Fixed double record stop messages when recording muted * 09/01/99 rodtoll Re-activated auto volume control * 09/02/99 rodtoll Re-activated and fixed old auto record volume code * 09/09/99 rodtoll Fixed bug in FSM that was causing transion to VA * state in almost every frame. * rodtoll Fixed recording level checks * 09/13/99 rodtoll Can now select old VA code with compiler define * rodtoll Minor fixes to automatic volume control * 09/28/99 rodtoll Modified to use new notification mechanism * rodtoll Added playback voice volume suppression * 09/29/99 pnewson Major AGC overhaul * 10/05/99 rodtoll Dropped AGC volume reduce threshold from 3s to 500ms. * 10/25/99 rodtoll Fix: Bug#114187 Always transmits first frame * Initial state looked like trailing frame, so always sent * 10/29/99 rodtoll Bug #113726 - Integrate Voxware Codecs, updating to use new * pluggable codec architecture. * 11/12/99 rodtoll Updated to use new recording classes, improved error * handling and new initialize function. * rodtoll Added new high CPU handling code for record. * Checks for lockup AND ignores frame if pointer hasn't moved forward * 11/16/99 rodtoll Recording thread now loops everytime it wakes up until it * has compressed and transmitted all the data it can before * going back to sleep. * rodtoll Now displays instrumentation data on recording thread * 11/18/99 rodtoll Re-activated recording pointer lockup detection code * 12/01/99 rodtoll Fix: Bug #121053 Microphone auto-select not working * rodtoll Updated to use new parameters for SelectMicrophone function * 12/08/99 rodtoll Bug #121054 Integrate code for handling capture focus. * 12/16/99 rodtoll Removed voice suppression * 01/10/00 pnewson AGC and VA tuning * 01/14/2000 rodtoll Updated to handle new multiple targets and use new speech * packet formats. * 01/21/2000 pnewson Update to support new DVSOUNDCONFIG_TESTMODE internal flag * used to detect lockups quickly during wizard testing. * 01/31/2000 pnewson re-add support for absence of DVCLIENTCONFIG_AUTOSENSITIVITY flag * 02/08/2000 rodtoll Bug #131496 - Selecting DVTHRESHOLD_DEFAULT results in voice * never being detected * 02/17/2000 rodtoll Bug #133691 - Choppy audio - queue was not adapting * Added instrumentation * 03/03/2000 rodtoll Updated to handle alternative gamevoice build. * 03/28/2000 rodtoll Updated to remove uneeded locks which were causing deadlock * 04/05/2000 rodtoll Updated to use new async, no buffer copy sends, removed old transmit buffer * 04/17/2000 rodtoll Bug #31316 - Some soundcards pass test with no microphone -- * made recording system ignore first 3 frames of audio * 04/19/2000 rodtoll Added support for new DVSOUNDCONFIG_NORECVOLAVAILABLE flag * 04/19/2000 pnewson Fix to make AGC code work properly with VA off * 04/25/2000 pnewson Fix to improve responsiveness of AGC when volume level too low * 07/09/2000 rodtoll Added signature bytes * 07/18/2000 rodtoll Fixed bug w/capture focus -- GetCurrentPosition will return an error * when focus is lost -- this was causing a session lost * 08/18/2000 rodtoll Bug #42542 - DPVoice retrofit: Voice retrofit locks up after host migration * 08/29/2000 rodtoll Bug #43553 - Start() returns 0x80004005 after lockup * rodtoll Bug #43620 - DPVOICE: Recording buffer locks up on Aureal Vortex (VxD). * Updated reset procedure so it ignores Stop failures and if Start() fails * it tries resetting the recording system. * 08/31/2000 rodtoll Bug #43804 - DVOICE: dwSensitivity structure member is confusing - should be dwThreshold * 09/13/2000 rodtoll Bug #44845 - DXSTRESS: DPLAY: Access violation * rodtoll Regression fix which caused fails failures in loopback test * 10/17/2000 rodtoll Bug #47224 - DPVOICE: Recording lockup reset fails w/DSERR_ALLOCATED added sleep to allow driver to cleanup * 10/30/2000 rodtoll Same as above -- Update with looping attempting to re-allocate capture device * 01/26/2001 rodtoll WINBUG #293197 - DPVOICE: [STRESS} Stress applications cannot tell difference between out of memory and internal errors. * Remap DSERR_OUTOFMEMORY to DVERR_OUTOFMEMORY instead of DVERR_SOUNDINITFAILURE. * Remap DSERR_ALLOCATED to DVERR_PLAYBACKSYSTEMERROR instead of DVERR_SOUNDINITFAILURE. * 04/11/2001 rodtoll WINBUG #221494 DPVOICE: Updates to improve lockup detection --- * Reset record event when no work to do so we don't spin unessessarily as much * Add check to ensure frame where we grab audio but pos hasn't move we don't count towards lockup * Add check to ensure we enforce a timeout + a frame count for lockup purposes. * 04/21/2001 rodtoll MANBUG #50058 DPVOICE: VoicePosition: No sound for couple of seconds when position bars are moved * - Modified lockup detection to be time based, increased timeout to 600ms. * ***************************************************************************/
#include "dxvoicepch.h"
#define RECORD_MAX_RESETS 10
#define RECORD_MAX_TIMEOUT 2000
#define TESTMODE_MAX_TIMEOUT 500
#define RECORD_NUM_TARGETS_INIT 0
#define RECORD_NUM_TARGETS_GROW 10
#define RECORD_PASSES_BEFORE_LOCKUP 25
#define RECORD_RESET_PAUSE 500
#define RECORD_RESET_ALLOC_ATTEMPTS 10
// RECORD_LOCKUP_TIMEOUT
//
// # of ms of no movement before a lockup is detected.
//
#define RECORD_LOCKUP_TIMEOUT 600
// Comment out to use the old sensitivity detection
#define __USENEWVA
// RECORDTEST_MIN_POWER / RECORDTEST_MAX_POWER
//
// Define the max and min possible power values
//#define RECORDTEST_MIN_POWER 0
//#define RECORDTEST_MAX_POWER 100
// We have to double the # because IsMuted is called twice / pass
#define RECORDTEST_NUM_FRAMESBEFOREVOICE 6
#undef DPF_MODNAME
#define DPF_MODNAME "CClientRecordSubSystem::CClientRecordSubSystem"
// CClientRecordSubSystem
//
// This is the constructor for the CClientRecordSubSystem class.
// It intiailizes the classes member variables with the appropriate
// values.
//
// Parameters:
// CDirectVoiceClientEngine *clientEngine -
// Pointer to the client object which is using this object.
//
// Returns:
// N/A
//
CClientRecordSubSystem::CClientRecordSubSystem( CDirectVoiceClientEngine *clientEngine ): m_dwSignature(VSIG_CLIENTRECORDSYSTEM), m_recordState(RECORDSTATE_IDLE), m_converter(NULL), m_remain(0), m_clientEngine(clientEngine), m_dwCurrentPower(0), m_dwSilentTime(0), m_lastFrameTransmitted(FALSE), m_msgNum(0), m_seqNum(0), m_dwLastTargetVersion(0), m_lSavedVolume(0), m_fRecordVolumeSaved(FALSE), m_uncompressedSize(0), m_compressedSize(0), m_framesPerPeriod(0), m_pbConstructBuffer(NULL), m_dwResetCount(0), m_dwNextReadPos(0), m_fIgnoreFrame(FALSE), m_dwPassesSincePosChange(0), m_fLostFocus(FALSE), m_pagcva(NULL), m_dwFrameCount(0), m_prgdvidTargetCache(NULL), m_dwTargetCacheSize(0), m_dwTargetCacheEntries(0), m_dwFullBufferSize(0) { }
#undef DPF_MODNAME
#define DPF_MODNAME "CClientRecordSubSystem::Initialize"
HRESULT CClientRecordSubSystem::Initialize() { HRESULT hr;
hr = DVCDB_CreateConverter( m_clientEngine->m_audioRecordBuffer->GetRecordFormat(), m_clientEngine->m_lpdvfCompressionInfo->guidType, &m_converter );
if( FAILED( hr ) ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to create converter. hr = 0x%x", hr ); return hr ; }
hr = m_converter->GetUnCompressedFrameSize( &m_uncompressedSize );
if( FAILED( hr ) ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to get size hr=0x%x", hr ); return hr; } hr = m_converter->GetCompressedFrameSize( &m_compressedSize );
if( FAILED( hr ) ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to get size hr=0x%x", hr ); return hr; } hr = m_converter->GetNumFramesPerBuffer( &m_framesPerPeriod );
if( FAILED( hr ) ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to get size. hr = 0x%x", hr ); return hr; } m_transmitFrame = FALSE; m_currentBuffer = 0; m_dwResetCount = 0; m_dwPassesSincePosChange = 0;
m_dwLastFrameTime = GetTickCount(); m_dwFrameTime = m_clientEngine->m_lpdvfCompressionInfo->dwTimeout; m_dwSilenceTimeout = m_clientEngine->m_lpdvfCompressionInfo->dwTrailFrames*m_dwFrameTime;
// Prevents first frame from transmitting all the time
m_dwSilentTime = m_dwSilenceTimeout+1;
if( m_clientEngine->m_audioRecordBuffer->GetRecordFormat()->wBitsPerSample == 8 ) { m_eightBit = TRUE; } else { m_eightBit = FALSE; }
m_dwFullBufferSize = m_uncompressedSize*m_framesPerPeriod;
m_pbConstructBuffer = new BYTE[m_dwFullBufferSize];
if( m_pbConstructBuffer == NULL ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Memory alloc failure" ); return DVERR_OUTOFMEMORY; }
BeginStats(); InitStats();
// create and init the AGC and VA algorithm class
m_pagcva = new CAGCVA1(); if (m_pagcva == NULL) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Memory alloc failure" ); return DVERR_OUTOFMEMORY; }
LONG lSavedAGCVolume; hr = m_pagcva->Init( m_clientEngine->s_szRegistryPath, m_clientEngine->m_dvClientConfig.dwFlags, m_clientEngine->m_dvSoundDeviceConfig.guidCaptureDevice, m_clientEngine->m_audioRecordBuffer->GetRecordFormat()->nSamplesPerSec, m_eightBit ? 8 : 16, &lSavedAGCVolume, m_clientEngine->m_dvClientConfig.dwThreshold); if (FAILED(hr)) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Error initializing AGC and/or VA algorithm, code: %i", hr); delete m_pagcva; return hr; } if( m_clientEngine->m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_AUTOSELECT ) { m_clientEngine->m_audioRecordBuffer->SelectMicrophone(TRUE); }
m_dwLastTargetVersion = m_clientEngine->m_dwTargetVersion;
if( !(m_clientEngine->m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_NORECVOLAVAILABLE) ) { // save the original volume settings
m_clientEngine->m_audioRecordBuffer->GetVolume( &m_lSavedVolume ); m_fRecordVolumeSaved = TRUE;
// set our initial volume
if (m_clientEngine->m_dvClientConfig.dwFlags & DVCLIENTCONFIG_AUTORECORDVOLUME) { m_clientEngine->m_dvClientConfig.lRecordVolume = lSavedAGCVolume; }
m_clientEngine->m_audioRecordBuffer->SetVolume( m_clientEngine->m_dvClientConfig.lRecordVolume ); } else { m_fRecordVolumeSaved = FALSE; }
// So our next
m_dwNextReadPos = 0; m_dwLastReadPos = 0; //m_uncompressedSize*(m_framesPerPeriod-1);
m_dwLastBufferPos = m_dwLastReadPos;
return DV_OK; }
#undef DPF_MODNAME
#define DPF_MODNAME "CClientRecordSubSystem::CleanupForReset"
HRESULT CClientRecordSubSystem::CleanupForReset() { HRESULT hr; if( m_converter != NULL ) { m_converter->Release(); m_converter = NULL; }
if( m_clientEngine->m_audioRecordBuffer ) { // Stop recording
m_clientEngine->m_audioRecordBuffer->Stop();
if( m_fRecordVolumeSaved ) { m_clientEngine->m_audioRecordBuffer->SetVolume( m_lSavedVolume ); } }
// Deinit and cleanup the AGC and VA algorthms
if (m_pagcva != NULL) { hr = m_pagcva->Deinit(); if (FAILED(hr)) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Deinit error on AGC and/or VA algorithm, code: %i", hr); } delete m_pagcva; m_pagcva = NULL; } else { DPFX(DPFPREP, DVF_ERRORLEVEL, "Unexpected NULL pointer for AGC/VA algorithm"); }
if( m_pbConstructBuffer != NULL ) { delete [] m_pbConstructBuffer; m_pbConstructBuffer = NULL; }
if( m_prgdvidTargetCache ) { delete [] m_prgdvidTargetCache; m_prgdvidTargetCache = NULL; m_dwTargetCacheSize = 0; m_dwTargetCacheEntries = 0; }
return DV_OK; }
#undef DPF_MODNAME
#define DPF_MODNAME "CClientRecordSubSystem::~CClientRecordSubSystem"
// CClientRecordSubSystem
//
// This is the destructor for the CClientRecordSubSystem
// class. It cleans up the allocated memory for the class
// and stops the recording device.
//
// Parameters:
// N/A
//
// Returns:
// N/A
//
CClientRecordSubSystem::~CClientRecordSubSystem() { HRESULT hr;
CleanupForReset();
CompleteStats();
m_dwSignature = VSIG_CLIENTRECORDSYSTEM_FREE; }
#undef DPF_MODNAME
#define DPF_MODNAME "CClientRecordSubSystem::IsMuted"
BOOL CClientRecordSubSystem::IsMuted() { // If the notification of the local player hasn't been processed and we're in peer to peer mode, don't
// allow recording to start until after the player has been indicated
if( !m_clientEngine->m_fLocalPlayerAvailable ) { DPFX(DPFPREP, RRI_DEBUGOUTPUT_LEVEL, "Local player has not yet been indicated, ignoring frame" ); return TRUE; } BOOL fMuted = FALSE;
// if(m_clientEngine->m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_TESTMODE )
// {
if( m_dwFrameCount < RECORDTEST_NUM_FRAMESBEFOREVOICE ) { DPFX(DPFPREP, RRI_DEBUGOUTPUT_LEVEL, "Skipping first %d frames for startup burst", RECORDTEST_NUM_FRAMESBEFOREVOICE ); m_dwFrameCount++; return TRUE; } // }
if( m_clientEngine->m_dvClientConfig.dwFlags & DVCLIENTCONFIG_ECHOSUPPRESSION ) { DNEnterCriticalSection( &m_clientEngine->m_lockPlaybackMode );
if( (m_clientEngine->m_dvClientConfig.dwFlags & DVCLIENTCONFIG_RECORDMUTE) || (m_clientEngine->m_dwEchoState == DVCECHOSTATE_PLAYBACK) ) { fMuted = TRUE; }
DNLeaveCriticalSection( &m_clientEngine->m_lockPlaybackMode ); } else { fMuted = (m_clientEngine->m_dvClientConfig.dwFlags & DVCLIENTCONFIG_RECORDMUTE); }
return fMuted; }
#undef DPF_MODNAME
#define DPF_MODNAME "CClientRecordSubSystem::StartMessage()"
void CClientRecordSubSystem::StartMessage() { BYTE bPeakLevel; if( m_clientEngine->m_dvClientConfig.dwFlags & DVCLIENTCONFIG_AUTOVOICEACTIVATED || m_clientEngine->m_dvClientConfig.dwFlags & DVCLIENTCONFIG_MANUALVOICEACTIVATED ) { m_pagcva->PeakResults(&bPeakLevel); } else { bPeakLevel = 0; }
DVMSG_RECORDSTART dvRecordStart; dvRecordStart.dwPeakLevel = bPeakLevel; dvRecordStart.dwSize = sizeof( DVMSG_RECORDSTART ); dvRecordStart.pvLocalPlayerContext = m_clientEngine->m_pvLocalPlayerContext; m_clientEngine->NotifyQueue_Add( DVMSGID_RECORDSTART, &dvRecordStart, sizeof( DVMSG_RECORDSTART ) );
DPFX(DPFPREP, RRI_DEBUGOUTPUT_LEVEL, "RRI, Starting Message" );
// STATSBLOCK: Begin
m_clientEngine->m_pStatsBlob->m_recStats.m_dwNumMessages++; // STATSBLOCK: End
}
#undef DPF_MODNAME
#define DPF_MODNAME "CClientRecordSubSystem::EndMessage()"
void CClientRecordSubSystem::EndMessage() { BYTE bPeakLevel; if( m_clientEngine->m_dvClientConfig.dwFlags & DVCLIENTCONFIG_AUTOVOICEACTIVATED || m_clientEngine->m_dvClientConfig.dwFlags & DVCLIENTCONFIG_MANUALVOICEACTIVATED ) { m_pagcva->PeakResults(&bPeakLevel); } else { bPeakLevel = 0; }
// STATBLOCK: Begin
if( m_seqNum > m_clientEngine->m_pStatsBlob->m_recStats.m_dwMLMax ) { m_clientEngine->m_pStatsBlob->m_recStats.m_dwMLMax = m_seqNum; }
if( m_seqNum < m_clientEngine->m_pStatsBlob->m_recStats.m_dwMLMin ) { m_clientEngine->m_pStatsBlob->m_recStats.m_dwMLMin = m_seqNum; }
m_clientEngine->m_pStatsBlob->m_recStats.m_dwMLTotal += m_seqNum; // STATBLOCK: End
DVMSG_RECORDSTOP dvRecordStop; dvRecordStop.dwPeakLevel = bPeakLevel; dvRecordStop.dwSize = sizeof( DVMSG_RECORDSTOP ); dvRecordStop.pvLocalPlayerContext = m_clientEngine->m_pvLocalPlayerContext; m_clientEngine->NotifyQueue_Add( DVMSGID_RECORDSTOP, &dvRecordStop, sizeof( DVMSG_RECORDSTOP ) );
m_msgNum++; m_seqNum = 0;
DPFX(DPFPREP, RRI_DEBUGOUTPUT_LEVEL, "RRI, Ending Message" ); }
#undef DPF_MODNAME
#define DPF_MODNAME "CClientRecordSubSystem::CheckVA()"
BOOL CClientRecordSubSystem::CheckVA() { #ifdef __USENEWVA
// We've been muted, turn off the VA
if( IsMuted() ) { m_dwSilentTime = m_dwSilenceTimeout+1; return FALSE; }
BOOL m_fVoiceDetected; m_pagcva->VAResults(&m_fVoiceDetected); if (!m_fVoiceDetected) { // This prevents wrap-around on silence timeout
if( m_dwSilentTime <= m_dwSilenceTimeout ) { m_dwSilentTime += m_dwFrameTime; }
DPFX(DPFPREP, DVF_INFOLEVEL, "### Silence Time to %d", m_dwSilentTime );
if( m_dwSilentTime > m_dwSilenceTimeout ) { DPFX(DPFPREP, DVF_INFOLEVEL, "### Silence Time exceeded %d", m_dwSilenceTimeout );
if( m_clientEngine->m_dvClientConfig.dwFlags & DVCLIENTCONFIG_ECHOSUPPRESSION ) { // If we're in the idle state, go to the recording state
DNEnterCriticalSection( &m_clientEngine->m_lockPlaybackMode );
if( m_clientEngine->m_dwEchoState == DVCECHOSTATE_RECORDING ) { DPFX(DPFPREP, RECORD_SWITCH_DEBUG_LEVEL, "%%%% Switching to idle mode" ); m_clientEngine->m_dwEchoState = DVCECHOSTATE_IDLE; }
DNLeaveCriticalSection( &m_clientEngine->m_lockPlaybackMode ); }
return FALSE; } } else { m_dwSilentTime = 0; DPFX(DPFPREP, DVF_INFOLEVEL, "### Silence Time to 0" ); DPFX(DPFPREP, DVF_INFOLEVEL, "### Transmit!!!!" ); }
if( m_clientEngine->m_dvClientConfig.dwFlags & DVCLIENTCONFIG_ECHOSUPPRESSION ) { // If we're in the idle state, go to the recording state
DNEnterCriticalSection( &m_clientEngine->m_lockPlaybackMode );
if( m_clientEngine->m_dwEchoState == DVCECHOSTATE_IDLE ) { DPFX(DPFPREP, RECORD_SWITCH_DEBUG_LEVEL, "%%%% Switching to recording mode" ); m_clientEngine->m_dwEchoState = DVCECHOSTATE_RECORDING; }
DNLeaveCriticalSection( &m_clientEngine->m_lockPlaybackMode ); }
return TRUE; #else
m_peakCheck = TRUE;
return !DetectSilence( m_bufferPtr, m_uncompressedSize, m_eightBit, 32, m_clientEngine->m_dwActivatePowerLevel, m_clientEngine->m_bLastPeak ); #endif
}
#undef DPF_MODNAME
#define DPF_MODNAME "CClientRecordSubSystem::DoAGC"
HRESULT CClientRecordSubSystem::DoAGC() { // let AGC have a bash at changing the volume, but only if AGC is enabled in the current client config.
if (m_clientEngine->m_dvClientConfig.dwFlags & DVCLIENTCONFIG_AUTORECORDVOLUME && !(m_clientEngine->m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_NORECVOLAVAILABLE) ) {
// get the current hardware volume level - this will allow us to track "outside" changes
// to the volume control. e.g. The user find the volume is too low, and manually drags the
// volume control's volume slider up a bit. We won't become totally confused, since we're
// checking the hardware level here.
m_clientEngine->m_audioRecordBuffer->GetVolume( &(m_clientEngine->m_dvClientConfig.lRecordVolume) );
LONG lNewVolume; m_pagcva->AGCResults(m_clientEngine->m_dvClientConfig.lRecordVolume, &lNewVolume, m_transmitFrame);
// set the current hardware volume level, but only if it has changed
if (m_clientEngine->m_dvClientConfig.lRecordVolume != lNewVolume) { // Problem: due to the convertion of logarithmic to linear
// volume settings, there may be a rounding error at low
// volumes. So, AGC will try to make a small volume adjustment
// that results in NO adjustment due to rounding, and it can't
// work it's way out of the hole.
//
// So - if AGC tried to uptick or downtick the volume, make
// sure it worked!
LONG lOrgVolume; LONG lVolumeDelta; LONG lNewHWVolume; lOrgVolume = m_clientEngine->m_dvClientConfig.lRecordVolume; lVolumeDelta = lNewVolume - lOrgVolume; lNewHWVolume = lOrgVolume; while(1) { m_clientEngine->m_dvClientConfig.lRecordVolume = lNewVolume; DPFX(DPFPREP, DVF_INFOLEVEL, "AGC: Setting volume to %i", lNewVolume); m_clientEngine->m_audioRecordBuffer->SetVolume(lNewVolume); m_clientEngine->m_audioRecordBuffer->GetVolume(&lNewHWVolume);
if (lNewHWVolume == lOrgVolume) { // The value did not change, so we're hitting a rounding
// error.
// Make sure we're not already at the min or max
if (lNewVolume == DSBVOLUME_MIN || lNewVolume == DSBVOLUME_MAX) { // There's nothing more we can do. Give up.
break; }
// Add another delta to the new volume, and try again.
lNewVolume += lVolumeDelta; if (lNewVolume > DSBVOLUME_MAX) { lNewVolume = DSBVOLUME_MAX; } if (lNewVolume < DSBVOLUME_MIN) { lNewVolume = DSBVOLUME_MIN; } } else { // The value changed, so we're done.
break; } } } } return DV_OK; }
#undef DPF_MODNAME
#define DPF_MODNAME "CClientRecordSubSystem::RecordFSM"
HRESULT CClientRecordSubSystem::RecordFSM() { if( m_fIgnoreFrame ) { return DV_OK; }
// In this case we NEVER transmit
// Shortcut to simplify the other cases
if( IsMuted() || !IsValidTarget() ) { // Go immediately to IDLE
DPFX(DPFPREP, DVF_INFOLEVEL, "### IsMuted || IsValidTarget --> IDLE" ); m_recordState = RECORDSTATE_IDLE; }
if( !IsMuted() ) { // before we analyze the data, push down the current
// relevant portions of the client config structure.
m_pagcva->SetSensitivity(m_clientEngine->m_dvClientConfig.dwFlags, m_clientEngine->m_dvClientConfig.dwThreshold); m_pagcva->AnalyzeData(m_bufferPtr, m_uncompressedSize); }
m_transmitFrame = FALSE;
switch( m_recordState ) { case RECORDSTATE_IDLE:
DPFX(DPFPREP, DVF_INFOLEVEL, "### STATE: IDLE" );
if( !IsMuted() && IsValidTarget() ) { if( IsPTT() ) { m_recordState = RECORDSTATE_PTT; m_transmitFrame = TRUE; } else { m_transmitFrame = CheckVA();
if( m_transmitFrame ) { m_recordState = RECORDSTATE_VA; } } }
break; case RECORDSTATE_VA:
DPFX(DPFPREP, DVF_INFOLEVEL, "### STATE: VA" );
if( IsPTT() ) { DPFX(DPFPREP, DVF_INFOLEVEL, "### VA --> PTT" ); m_recordState = RECORDSTATE_PTT; m_transmitFrame = TRUE; } else { m_transmitFrame = CheckVA(); if (!m_transmitFrame) { DPFX(DPFPREP, DVF_INFOLEVEL, "### !VA --> IDLE" ); m_recordState = RECORDSTATE_IDLE; } }
break;
case RECORDSTATE_PTT:
DPFX(DPFPREP, DVF_INFOLEVEL, "### STATE: PTT" );
if( IsVA() ) { DPFX(DPFPREP, DVF_INFOLEVEL, "### PTT --> VA" ); m_recordState = RECORDSTATE_VA; m_transmitFrame = CheckVA(); } else { m_transmitFrame = TRUE; }
break; }
// Now that we've figured out if we're transmitting or not, do the AGC
DoAGC();
// Message Ended
if( m_lastFrameTransmitted && !m_transmitFrame ) { EndMessage(); } // Message Started
else if( !m_lastFrameTransmitted && m_transmitFrame ) { StartMessage(); } // Message Continuing
else { // If the target has changed since the last frame
if( m_clientEngine->m_dwTargetVersion != m_dwLastTargetVersion ) { // If we're going to be transmitting
if( m_transmitFrame ) { EndMessage(); StartMessage(); } } }
m_lastFrameTransmitted = m_transmitFrame; m_dwLastTargetVersion = m_clientEngine->m_dwTargetVersion;
// Save the peak level to propogate up to the app
if( m_fIgnoreFrame ) { m_clientEngine->m_bLastPeak = 0; } else { m_pagcva->PeakResults(&(m_clientEngine->m_bLastPeak)); }
return DV_OK; }
#undef DPF_MODNAME
#define DPF_MODNAME "CClientRecordSubSystem::ResetForLockup"
// Reset For Lockup
//
// This function is called when an amount of time specified
// by the timeout is exceeded since the last movement of the
// recording buffer
//
// Parameters:
// N/A
//
// Returns:
// hr
//
HRESULT CClientRecordSubSystem::ResetForLockup() { HRESULT hr; DWORD dwBufferPos; Diagnostics_Write( DVF_ERRORLEVEL, "LOCKUP: Detected! Attempting RESET" ); hr = m_clientEngine->m_audioRecordBuffer->Stop();
if( FAILED( hr ) ) { Diagnostics_Write( DVF_ERRORLEVEL, "LOCKUP: Stop() Failed hr=0x%x", hr ); } else { Diagnostics_Write( DVF_ERRORLEVEL, "LOCKUP: Stop() worked", hr ); }
hr = m_clientEngine->m_audioRecordBuffer->Record( TRUE );
if( FAILED( hr ) ) { Diagnostics_Write( DVF_ERRORLEVEL, "LOCKUP: Record() failed hr=0x%x", hr );
// Cleanup for a FULL recording subsystem reset
hr = CleanupForReset();
if( FAILED( hr ) ) { Diagnostics_Write( DVF_ERRORLEVEL, "LOCKUP: Unable to cleanup for reset hr=0x%x", hr ); return hr; }
// Destroy / stop etc old buffer
delete m_clientEngine->m_audioRecordBuffer; m_clientEngine->m_audioRecordBuffer = NULL;
// Will now re-attempt to re-acquire the device a total of RECORD_RESET_ALLOC_ATTEMPTS times.
for( DWORD dwAttempt = 0; dwAttempt < RECORD_RESET_ALLOC_ATTEMPTS; dwAttempt++ ) {
// Ok. So why's this sleep here?
//
// Turns out on some systems / drivers destroying the buffer as above and then immediately trying to create a new
// one returns a DSERR_ALLOCATED (which ends up causing a session lost w/DVERR_RECORDSYSTEMERROR). There
// is likely some kind of timing problem in the drivers effected. Adding this sleep alleviates the problem probably
// because it gives the driver time to reset.
//
// The problem itself is hard to repro, so don't assume removing the assert and not seeing the problem means it
// doesn't happen anymore.
//
Sleep( RECORD_RESET_PAUSE );
// Create a new one
hr = InitializeRecordBuffer( m_clientEngine->m_dvSoundDeviceConfig.hwndAppWindow, m_clientEngine->m_lpdvfCompressionInfo, m_clientEngine->m_audioRecordDevice, &m_clientEngine->m_audioRecordBuffer, m_clientEngine->m_dvSoundDeviceConfig.dwFlags );
if( hr == DSERR_ALLOCATED ) { Diagnostics_Write( DVF_ERRORLEVEL, "LOCKUP: Unable to rebuild capture object, re-attempting.." ); continue; } else if( FAILED( hr ) ) { DSASSERT( FALSE ); Diagnostics_Write( DVF_ERRORLEVEL, "LOCKUP: Unable to restart locked up recording buffer hr=0x%x", hr ); return hr; } else if( SUCCEEDED(hr) ) { break; } }
DSASSERT( SUCCEEDED( hr ) );
// Restart the entire recording sub-system
hr = Initialize();
if( FAILED( hr ) ) { Diagnostics_Write( DVF_ERRORLEVEL, "LOCKUP: Unable to re-initialize recording sub-system hr=0x%x", hr ); return hr; }
Diagnostics_Write( DVF_ERRORLEVEL, "LOCKUP: Full reset worked!" ); } else { Diagnostics_Write( DVF_ERRORLEVEL, "LOCKUP: Start() succeeded" ); }
BOOL fLostFocus;
// We're going to ignore focus results because it will be picked up by the next
// pass through the loop.
//
hr = m_clientEngine->m_audioRecordBuffer->GetCurrentPosition( &dwBufferPos, &fLostFocus );
if( FAILED( hr ) ) { DSASSERT( FALSE ); DPFX(DPFPREP, DVF_ERRORLEVEL, "Get Current position failed" ); return hr; }
m_dwLastReadPos = dwBufferPos; //(m_uncompressedSize*(m_framesPerPeriod-1));
m_dwNextReadPos = dwBufferPos; // 0;
m_dwLastBufferPos = dwBufferPos; m_dwLastFrameTime = GetTickCount();
// Reset the record count -- this routine can take some time so the record
// count will queue up on a reset. Don't want too many queued events or
// we could end up with a false lockup detection.
//
ResetEvent( m_clientEngine->m_thTimerInfo.hRecordTimerEvent );
return DV_OK; }
#undef DPF_MODNAME
#define DPF_MODNAME "CClientRecordSubSystem::GetNextFrame"
// GetNextFrame
//
// This function retrieves the next frame from the recording input
// and detects recording lockup. If a recording lockup is detected
// then this function attempts to restart the recording system
// 3 times. If it fails the recording system is considered to be
// locked up.
//
// This was implemented as a fix for SB Live! cards which periodically
// lockup while recording. (Also noticed on Aureal Vortex cards).
//
// Parameters:
// N/A
//
// Returns:
// bool - true if next frame was retrieved, false if lockup detected
HRESULT CClientRecordSubSystem::GetNextFrame( LPBOOL pfContinue ) { HRESULT hr; DWORD dwBufferPos;
PVOID pBufferPtr1, pBufferPtr2; DWORD dwSizeBuffer1, dwSizeBuffer2; DWORD dwCurrentTime; BOOL fLostFocus; DWORD dwTSLM;
hr = m_clientEngine->m_audioRecordBuffer->GetCurrentPosition( &dwBufferPos, &fLostFocus );
if( FAILED( hr ) && !fLostFocus ) { DSASSERT( FALSE ); DPFX(DPFPREP, DVF_ERRORLEVEL, "Get Current position failed" ); return hr; }
// Get the current time
dwCurrentTime = GetTickCount();
// The focus has changed!
if( fLostFocus != m_fLostFocus ) { m_fLostFocus = fLostFocus;
// We just lost focus.
if( fLostFocus ) { DPFX(DPFPREP, RRI_DEBUGOUTPUT_LEVEL, "RRI, Buffer just lost focus. No more input until focus returns" );
// Keep frame time up to date since we are about to exit
m_dwLastFrameTime = dwCurrentTime;
// Notify the application
m_clientEngine->NotifyQueue_Add( DVMSGID_LOSTFOCUS, NULL, 0 );
// If we're in a message, end it. Won't be any more input until focus returns.
if( m_lastFrameTransmitted ) { EndMessage(); m_lastFrameTransmitted = FALSE; }
// Set the peak levels to 0
m_clientEngine->m_bLastPeak = 0;
// To ignore the frame
m_fIgnoreFrame = TRUE;
// To shortcircuit recording loop
*pfContinue = FALSE;
// Drop the data remaining in the buffer
if( dwBufferPos < m_uncompressedSize ) { m_dwLastReadPos = (m_dwFullBufferSize) - (m_uncompressedSize - dwBufferPos); } else { m_dwLastReadPos = (dwBufferPos - m_uncompressedSize ); } m_dwNextReadPos = dwBufferPos;
DPFX(DPFPREP, RRI_DEBUGOUTPUT_LEVEL, "RRI, Moving last read to %d and next to %d", m_dwLastReadPos, m_dwNextReadPos );
return DV_OK; } // We just gained focus... continue as if normal.
else { m_clientEngine->NotifyQueue_Add( DVMSGID_GAINFOCUS, NULL, 0 ); } }
if( fLostFocus ) { // Set the peak levels to 0
m_clientEngine->m_bLastPeak = 0;
// Ignore this frame
m_fIgnoreFrame = TRUE;
// Do not continue in recording loop until next wakeup
*pfContinue = FALSE;
// Keep frame time up to date since we are about to exit
m_dwLastFrameTime = dwCurrentTime;
// Track the moving buffer (if it's moving) for the case of WDM
// drivers on Millenium that continue to record
if( dwBufferPos < m_uncompressedSize ) { m_dwLastReadPos = (m_dwFullBufferSize) - (m_uncompressedSize - dwBufferPos); } else { m_dwLastReadPos = (dwBufferPos - m_uncompressedSize ); } m_dwNextReadPos = dwBufferPos;
return DV_OK; } // STATSBLOCK: Begin
dwTSLM = dwCurrentTime - m_dwLastFrameTime; m_clientEngine->m_pStatsBlob->m_recStats.m_dwRTSLMTotal += dwTSLM; if( dwTSLM > m_clientEngine->m_pStatsBlob->m_recStats.m_dwRTSLMMax ) { m_clientEngine->m_pStatsBlob->m_recStats.m_dwRTSLMMax = dwTSLM; }
if( dwTSLM < m_clientEngine->m_pStatsBlob->m_recStats.m_dwRTSLMMin ) { m_clientEngine->m_pStatsBlob->m_recStats.m_dwRTSLMMin = dwTSLM; } // STATSBLOCK: End
DPFX(DPFPREP, DVF_INFOLEVEL, "Current Pos: %d, Last Pos: %d", dwBufferPos, m_dwLastBufferPos );
// Calculate how much buffer we have to catch up for.
if( m_dwNextReadPos > dwBufferPos ) { dwSizeBuffer2 = ((m_dwFullBufferSize)-m_dwNextReadPos)+dwBufferPos; } else { dwSizeBuffer2 = dwBufferPos - m_dwNextReadPos; }
// See if a lockup occurred which means the buffer hasn't moved
//
// We only check for a lockup however when we aren't in the process of "Catching up". If we've
// fallen behind because we lost CPU, and we're several frames behind we're going to run through
// this section several times to catch up. The issue is that when we run several times there is
// very little time between the runs and therefore it is perfectly reasonable for DirectSound
// to have not moved it's buffer pointer.
//
if( m_dwLastBufferPos == dwBufferPos && dwSizeBuffer2 < m_uncompressedSize ) // Make sure we're not just "catching up".
{ m_dwPassesSincePosChange++;
// The lower this number is, the better the experience for people with bad drivers that lockup.
// The higher it is, the more we think we've locked up in situations where the buffer legitimately
// doesn't move, like high CPU usage, or when the app is in the debugger.
if( (dwCurrentTime - m_dwLastFrameTime) >= RECORD_LOCKUP_TIMEOUT ) { m_dwPassesSincePosChange = 0;
// We've been RECORD_MAX_RESETS passes through here without the buffer moving, reset
if( m_dwResetCount > RECORD_MAX_RESETS ) { DPFX(DPFPREP, RRI_DEBUGOUTPUT_LEVEL, "RRI, Maximum Resets exceeded" ); return DVERR_RECORDSYSTEMERROR; } DPFX(DPFPREP, RRI_DEBUGOUTPUT_LEVEL, "RRI, Lockup Detected %d ms since last movement", dwCurrentTime - m_dwLastFrameTime );
// STATSBLOCK: Begin
DPFX(DPFPREP, DVF_GLITCH_DEBUG_LEVEL, "GLITCH: Record: Recording buffer has stopped moving." ); // STATSBLOCK: End
hr = ResetForLockup();
if( FAILED( hr ) ) { DPFX(DPFPREP, RRI_DEBUGOUTPUT_LEVEL, "RRI, Reset for lockup failed hr=0x%x", hr ); return hr; }
m_fIgnoreFrame = TRUE; *pfContinue = FALSE; m_dwResetCount++;
// STATSBLOCK: Begin
m_clientEngine->m_pStatsBlob->m_recStats.m_dwRRTotal++;
if( m_dwResetCount < m_clientEngine->m_pStatsBlob->m_recStats.m_dwRRMin ) { m_clientEngine->m_pStatsBlob->m_recStats.m_dwRRMin = m_dwResetCount; }
if( m_dwResetCount > m_clientEngine->m_pStatsBlob->m_recStats.m_dwRRMax ) { m_clientEngine->m_pStatsBlob->m_recStats.m_dwRRMax = m_dwResetCount; } // STATSBLOCK: End
// Keep frame time up to date since we are about to exit
//
// NOTE: This needs to be GetTickCount() because Reset() can take 100's of ms.
m_dwLastFrameTime = GetTickCount(); return DV_OK; } } else { m_dwPassesSincePosChange = 0; }
// There was no lockup, and we are in focus, continue
m_dwLastBufferPos = dwBufferPos;
// Calc Delta in bytes of buffer read pointer
if( m_dwLastBufferPos > dwBufferPos ) { dwSizeBuffer1 = ((m_dwFullBufferSize)-m_dwLastBufferPos)+dwBufferPos; } else { dwSizeBuffer1 = dwBufferPos - m_dwLastBufferPos; }
// The position did not move enough for a full frame.
if( dwSizeBuffer2 < m_uncompressedSize ) { m_fIgnoreFrame = TRUE; *pfContinue = FALSE;
DPFX(DPFPREP, RRI_DEBUGOUTPUT_LEVEL, "RRI, %d, %d, %d, %d, %d, %d (SKIPPING)", dwBufferPos, dwSizeBuffer1, dwCurrentTime - m_dwLastFrameTime, m_dwNextReadPos, dwSizeBuffer2, m_uncompressedSize ); } else { DPFX(DPFPREP, RRI_DEBUGOUTPUT_LEVEL, "RRI, %d, %d, %d, %d, %d, %d", dwBufferPos, dwSizeBuffer1, dwCurrentTime - m_dwLastFrameTime, m_dwNextReadPos, dwSizeBuffer2, m_uncompressedSize );
m_dwResetCount = 0;
// STATSBLOCK: BEGIN
m_clientEngine->m_pStatsBlob->m_recStats.m_dwRMMSTotal += (dwCurrentTime - m_dwLastFrameTime);
if( (dwCurrentTime - m_dwLastFrameTime) > m_clientEngine->m_pStatsBlob->m_recStats.m_dwRMMSMax ) { m_clientEngine->m_pStatsBlob->m_recStats.m_dwRMMSMax = dwCurrentTime - m_dwLastFrameTime; }
if( m_clientEngine->m_pStatsBlob->m_recStats.m_dwRMMSMax > 70000 ) { }
if( (dwCurrentTime - m_dwLastFrameTime) < m_clientEngine->m_pStatsBlob->m_recStats.m_dwRMMSMin ) { m_clientEngine->m_pStatsBlob->m_recStats.m_dwRMMSMin = dwCurrentTime - m_dwLastFrameTime; } m_clientEngine->m_pStatsBlob->m_recStats.m_dwRMBTotal += dwSizeBuffer1;
if( dwSizeBuffer1 > m_clientEngine->m_pStatsBlob->m_recStats.m_dwRMBMax ) { m_clientEngine->m_pStatsBlob->m_recStats.m_dwRMBMax = dwSizeBuffer1; }
if( dwSizeBuffer1 < m_clientEngine->m_pStatsBlob->m_recStats.m_dwRMBMin ) { m_clientEngine->m_pStatsBlob->m_recStats.m_dwRMBMin = dwSizeBuffer1; } m_clientEngine->m_pStatsBlob->m_recStats.m_dwRLTotal += dwSizeBuffer2;
if( dwSizeBuffer2 > m_clientEngine->m_pStatsBlob->m_recStats.m_dwRLMax ) { m_clientEngine->m_pStatsBlob->m_recStats.m_dwRLMax = dwSizeBuffer2; }
if( dwSizeBuffer2 < m_clientEngine->m_pStatsBlob->m_recStats.m_dwRLMin ) { m_clientEngine->m_pStatsBlob->m_recStats.m_dwRLMin = dwSizeBuffer2; } // STATSBLOCK: END
// Get the next block of audio data and construct it into the buffer
hr = m_clientEngine->m_audioRecordBuffer->Lock( m_dwNextReadPos, m_uncompressedSize, &pBufferPtr1, &dwSizeBuffer1, &pBufferPtr2, &dwSizeBuffer2, 0 );
if( FAILED( hr ) ) { DSASSERT( FALSE ); DPFX(DPFPREP, DVF_ERRORLEVEL, "Error locking buffer: loc=%d size=%d hr=0x%x", m_dwNextReadPos, m_uncompressedSize, hr ); return hr; }
memcpy( m_pbConstructBuffer, pBufferPtr1, dwSizeBuffer1 );
if( dwSizeBuffer2 > 0 ) { memcpy( &m_pbConstructBuffer[dwSizeBuffer1], pBufferPtr2, dwSizeBuffer2 ); }
hr = m_clientEngine->m_audioRecordBuffer->UnLock( pBufferPtr1, dwSizeBuffer1, pBufferPtr2, dwSizeBuffer2 );
if( FAILED( hr ) ) { DSASSERT( FALSE ); DPFX(DPFPREP, DVF_ERRORLEVEL, "Error unlocking buffer hr=0x%x", hr ); return hr; }
m_bufferPtr = m_pbConstructBuffer;
m_fIgnoreFrame = FALSE;
m_dwLastReadPos = m_dwNextReadPos; m_dwNextReadPos += m_uncompressedSize; m_dwNextReadPos %= (m_dwFullBufferSize);
// Update the last frame time
m_dwLastFrameTime = dwCurrentTime;
*pfContinue = TRUE; }
return DV_OK; }
#undef DPF_MODNAME
#define DPF_MODNAME "CClientRecordSubSystem::TransmitFrame"
// TransmitFrame
//
// This function looks at the state of the FSM, and if the latest
// frame is to be transmitted it compresses it and transmits
// it to the server (cleint/server mode) or to all the players
// (or player if whispering) in peer to peer mode. If the frame
// is not to be transmitted this function does nothing.
//
// It is also responsible for ensuring any microphone clicks are
// mixed into the audio stream.
//
// This function also updates the transmission statisics and
// the current transmission status.
//
// Parameters:
// N/A
//
// Returns:
// bool - always returns true at the moment
HRESULT CClientRecordSubSystem::TransmitFrame() { HRESULT hr = DV_OK;
if( m_transmitFrame && !m_fIgnoreFrame ) { // STATSBLOCK: Begin
m_clientEngine->m_pStatsBlob->m_recStats.m_dwSentFrames++; // STATSBLOCK: End
if( m_clientEngine->m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_PEER ) { hr = BuildAndTransmitSpeechHeader(FALSE); } else if( m_clientEngine->m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_ECHO ) { hr = BuildAndTransmitSpeechHeader(TRUE); } else { hr = BuildAndTransmitSpeechWithTarget(TRUE); } } else { // STATSBLOCK: Begin
m_clientEngine->m_pStatsBlob->m_recStats.m_dwIgnoredFrames++; // STATSBLOCK: End
}
return hr; }
#undef DPF_MODNAME
#define DPF_MODNAME "CClientRecordSubSystem::BuildAndTransmitSpeechHeader"
HRESULT CClientRecordSubSystem::BuildAndTransmitSpeechHeader( BOOL bSendToServer ) { PDVPROTOCOLMSG_SPEECHHEADER pdvSpeechHeader; HRESULT hr; DWORD dwTargetSize; DWORD dwStartTime; DWORD dwCompressTime; PDVTRANSPORT_BUFFERDESC pBufferDesc; LPVOID pvSendContext;
pBufferDesc = m_clientEngine->GetTransmitBuffer( sizeof(DVPROTOCOLMSG_SPEECHHEADER)+COMPRESSION_SLUSH+m_compressedSize, &pvSendContext );
if( pBufferDesc == NULL ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to get buffer for transmission" ); return DVERR_OUTOFMEMORY; }
pdvSpeechHeader = (PDVPROTOCOLMSG_SPEECHHEADER) pBufferDesc->pBufferData;
pdvSpeechHeader->dwType = DVMSGID_SPEECH; pdvSpeechHeader->bMsgNum = m_msgNum; pdvSpeechHeader->bSeqNum = m_seqNum;
DPFX(DPFPREP, DVF_CLIENT_SEQNUM_DEBUG_LEVEL, "SEQ: Record: Msg [%d] Seq [%d]", m_msgNum, m_seqNum );
dwTargetSize = m_compressedSize;
dwStartTime = GetTickCount(); hr = m_converter->Convert( (BYTE *) m_bufferPtr, m_uncompressedSize, (BYTE *) &pdvSpeechHeader[1], &dwTargetSize, FALSE );
if( FAILED( hr ) ) { m_clientEngine->ReturnTransmitBuffer( pvSendContext ); DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to perform conversion hr=0x%x", hr ); return hr; }
dwCompressTime = GetTickCount() - dwStartTime;
DPFX(DPFPREP, DVF_INFOLEVEL, "Compressed %d bytes to %d taking %d ms", m_uncompressedSize, dwTargetSize, dwCompressTime );
// STATSBLOCK: Begin
m_clientEngine->m_pStatsBlob->m_recStats.m_dwCTTotal += dwCompressTime;
if( dwCompressTime < m_clientEngine->m_pStatsBlob->m_recStats.m_dwCTMin ) { m_clientEngine->m_pStatsBlob->m_recStats.m_dwCTMin = dwCompressTime; }
if( dwCompressTime > m_clientEngine->m_pStatsBlob->m_recStats.m_dwCTMax ) { m_clientEngine->m_pStatsBlob->m_recStats.m_dwCTMax = dwCompressTime; }
m_clientEngine->m_pStatsBlob->m_recStats.m_dwCSTotal += dwTargetSize;
if( dwTargetSize < m_clientEngine->m_pStatsBlob->m_recStats.m_dwCSMin ) { m_clientEngine->m_pStatsBlob->m_recStats.m_dwCSMin = dwTargetSize; }
if( dwTargetSize > m_clientEngine->m_pStatsBlob->m_recStats.m_dwCSMax ) { m_clientEngine->m_pStatsBlob->m_recStats.m_dwCSMax = dwTargetSize; }
// Header is fixed size
m_clientEngine->m_pStatsBlob->m_recStats.m_dwHSTotal += sizeof( DVPROTOCOLMSG_SPEECHHEADER ); m_clientEngine->m_pStatsBlob->m_recStats.m_dwHSMax = sizeof( DVPROTOCOLMSG_SPEECHHEADER ); m_clientEngine->m_pStatsBlob->m_recStats.m_dwHSMin = sizeof( DVPROTOCOLMSG_SPEECHHEADER ); // STATSBLOCK: End
if( m_clientEngine->m_dwNumTargets == 0 ) { DPFX(DPFPREP, DVF_INFOLEVEL, "Targets set to NONE since FSM. Not transmitting" ); m_clientEngine->ReturnTransmitBuffer( pvSendContext ); return DV_OK; } else { pBufferDesc->dwBufferSize = sizeof(DVPROTOCOLMSG_SPEECHHEADER)+dwTargetSize; if( bSendToServer ) { DPFX(DPFPREP, DVF_INFOLEVEL, "Transmitting %d bytes to server", pBufferDesc->dwBufferSize ); hr = m_clientEngine->m_lpSessionTransport->SendToServer( pBufferDesc, pvSendContext, 0 ); } else { // Copy the target list to a cache so we can drop the lock during the send.
DNEnterCriticalSection( &m_clientEngine->m_csTargetLock ); if( m_clientEngine->m_dwNumTargets > m_dwTargetCacheSize ) { if( m_prgdvidTargetCache ) delete [] m_prgdvidTargetCache; m_dwTargetCacheSize = m_clientEngine->m_dwNumTargets; m_prgdvidTargetCache = new DVID[m_dwTargetCacheSize];
if( !m_prgdvidTargetCache ) { DNLeaveCriticalSection( &m_clientEngine->m_csTargetLock ); DPFERR( "Out of memory" ); return DVERR_OUTOFMEMORY; } } memcpy( m_prgdvidTargetCache, m_clientEngine->m_pdvidTargets, sizeof(DVID)*m_clientEngine->m_dwNumTargets ); m_dwTargetCacheEntries = m_clientEngine->m_dwNumTargets; DNLeaveCriticalSection( &m_clientEngine->m_csTargetLock );
// the target cache (prgdvidTargetCache) doesn't need protection since it is only accessed by one thread at a time.
DPFX(DPFPREP, DVF_INFOLEVEL, "Transmitting %d bytes to target list", pBufferDesc->dwBufferSize ); hr = m_clientEngine->m_lpSessionTransport->SendToIDS( m_prgdvidTargetCache, m_dwTargetCacheEntries, pBufferDesc, pvSendContext, 0 ); }
if( hr == DVERR_PENDING ) { hr = DV_OK; } else if ( FAILED( hr )) { DPFX(DPFPREP, DVF_INFOLEVEL, "Send failed hr=0x%x", hr ); }
m_seqNum++; }
return hr; }
#undef DPF_MODNAME
#define DPF_MODNAME "CClientRecordSubSystem::BuildAndTransmitSpeechWithTarget"
HRESULT CClientRecordSubSystem::BuildAndTransmitSpeechWithTarget( BOOL bSendToServer ) { PDVPROTOCOLMSG_SPEECHWITHTARGET pdvSpeechWithTarget; HRESULT hr; DWORD dwTargetSize; DWORD dwTransmitSize; DWORD dwTargetInfoSize; PBYTE pbBuilderLoc; DWORD dwStartTime; DWORD dwCompressTime; PDVTRANSPORT_BUFFERDESC pBufferDesc; LPVOID pvSendContext;
dwTransmitSize = sizeof( DVPROTOCOLMSG_SPEECHWITHTARGET );
// Calculate size we'll need for storing the targets
DNEnterCriticalSection( &m_clientEngine->m_csTargetLock );
dwTargetInfoSize = sizeof( DVID ) * m_clientEngine->m_dwNumTargets;
if( dwTargetInfoSize == 0 ) { DPFX(DPFPREP, DVF_INFOLEVEL, "Targets set to NONE since FSM. Not transmitting" ); DNLeaveCriticalSection( &m_clientEngine->m_csTargetLock );
return DV_OK; } else { dwTransmitSize += dwTargetInfoSize; }
DPFX(DPFPREP, DVF_CLIENT_SEQNUM_DEBUG_LEVEL, "SEQ: Record: Msg [%d] Seq [%d]", m_msgNum, m_seqNum );
dwTransmitSize += m_compressedSize;
pBufferDesc = m_clientEngine->GetTransmitBuffer( sizeof(DVPROTOCOLMSG_SPEECHWITHTARGET)+m_compressedSize+COMPRESSION_SLUSH+dwTransmitSize, &pvSendContext );
if( pBufferDesc == NULL ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to get buffer for transmission" ); return DVERR_OUTOFMEMORY; }
pdvSpeechWithTarget = (PDVPROTOCOLMSG_SPEECHWITHTARGET) pBufferDesc->pBufferData; pbBuilderLoc = (PBYTE) &pdvSpeechWithTarget[1];
memcpy( pbBuilderLoc, m_clientEngine->m_pdvidTargets, dwTargetInfoSize );
pdvSpeechWithTarget->dwNumTargets = m_clientEngine->m_dwNumTargets;
DNLeaveCriticalSection( &m_clientEngine->m_csTargetLock );
pbBuilderLoc += dwTargetInfoSize;
pdvSpeechWithTarget->dvHeader.dwType = DVMSGID_SPEECHWITHTARGET; pdvSpeechWithTarget->dvHeader.bMsgNum = m_msgNum; pdvSpeechWithTarget->dvHeader.bSeqNum = m_seqNum;
dwTargetSize = m_compressedSize;
dwStartTime = GetTickCount();
DPFX(DPFPREP, DVF_COMPRESSION_DEBUG_LEVEL, "COMPRESS: < %d --> %d ", m_uncompressedSize, dwTargetSize );
hr = m_converter->Convert( (BYTE *) m_bufferPtr, m_uncompressedSize, (BYTE *) pbBuilderLoc, &dwTargetSize, FALSE );
if( FAILED( hr ) ) { m_clientEngine->ReturnTransmitBuffer( pvSendContext ); DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to perform conversion hr=0x%x", hr ); return hr; }
dwCompressTime = GetTickCount() - dwStartTime;
DPFX(DPFPREP, DVF_COMPRESSION_DEBUG_LEVEL, "COMPRESS: > %d --> %d %d ms", m_uncompressedSize, dwTargetSize, dwCompressTime );
// STATSBLOCK: Begin
m_clientEngine->m_pStatsBlob->m_recStats.m_dwCTTotal += dwCompressTime;
if( dwCompressTime < m_clientEngine->m_pStatsBlob->m_recStats.m_dwCTMin ) { m_clientEngine->m_pStatsBlob->m_recStats.m_dwCTMin = dwCompressTime; }
if( dwCompressTime > m_clientEngine->m_pStatsBlob->m_recStats.m_dwCTMax ) { m_clientEngine->m_pStatsBlob->m_recStats.m_dwCTMax = dwCompressTime; } m_clientEngine->m_pStatsBlob->m_recStats.m_dwCSTotal += dwTargetSize;
if( dwTargetSize < m_clientEngine->m_pStatsBlob->m_recStats.m_dwCSMin ) { m_clientEngine->m_pStatsBlob->m_recStats.m_dwCSMin = dwTargetSize; }
if( dwTargetSize > m_clientEngine->m_pStatsBlob->m_recStats.m_dwCSMax ) { m_clientEngine->m_pStatsBlob->m_recStats.m_dwCSMax = dwTargetSize; }
// Header stats
m_clientEngine->m_pStatsBlob->m_recStats.m_dwHSTotal += sizeof( DVPROTOCOLMSG_SPEECHWITHTARGET )+dwTargetInfoSize;
if( sizeof( DVPROTOCOLMSG_SPEECHWITHTARGET )+dwTargetInfoSize > m_clientEngine->m_pStatsBlob->m_recStats.m_dwHSMax ) { m_clientEngine->m_pStatsBlob->m_recStats.m_dwHSMax = sizeof( DVPROTOCOLMSG_SPEECHWITHTARGET )+dwTargetInfoSize; }
if( sizeof( DVPROTOCOLMSG_SPEECHWITHTARGET )+dwTargetInfoSize < m_clientEngine->m_pStatsBlob->m_recStats.m_dwHSMin ) { m_clientEngine->m_pStatsBlob->m_recStats.m_dwHSMin = sizeof( DVPROTOCOLMSG_SPEECHWITHTARGET )+dwTargetInfoSize; }
// STATSBLOCK: End
// We need to transmit header, target info and then speech data
dwTransmitSize = sizeof( DVPROTOCOLMSG_SPEECHWITHTARGET ) + dwTargetInfoSize + dwTargetSize;
pBufferDesc->dwBufferSize = dwTransmitSize;
hr = m_clientEngine->m_lpSessionTransport->SendToServer( pBufferDesc, pvSendContext, 0 );
m_seqNum++;
if( hr == DVERR_PENDING ) { hr = DV_OK; } else if( FAILED( hr ) ) { DPFX(DPFPREP, DVF_INFOLEVEL, "Send failed hr=0x%x", hr ); }
return hr; }
#undef DPF_MODNAME
#define DPF_MODNAME "CClientRecordSubSystem::IsValidTarget"
BOOL CClientRecordSubSystem::IsValidTarget() { BOOL fValidTarget; DNEnterCriticalSection( &m_clientEngine->m_csTargetLock ); if( m_clientEngine->m_dwNumTargets > 0 ) { fValidTarget = TRUE; } else { fValidTarget = FALSE; } DNLeaveCriticalSection( &m_clientEngine->m_csTargetLock );
return fValidTarget; }
#undef DPF_MODNAME
#define DPF_MODNAME "CClientRecordSubSystem::InitStats"
void CClientRecordSubSystem::InitStats() { // STATSBLOCK: begin
// Setup record stats
m_clientEngine->m_pStatsBlob->m_recStats.m_dwFramesPerBuffer = m_framesPerPeriod; m_clientEngine->m_pStatsBlob->m_recStats.m_dwFrameTime = m_dwFrameTime; m_clientEngine->m_pStatsBlob->m_recStats.m_dwCSMin = 0xFFFFFFFF; m_clientEngine->m_pStatsBlob->m_recStats.m_dwUnCompressedSize = m_uncompressedSize; m_clientEngine->m_pStatsBlob->m_recStats.m_dwSilenceTimeout = m_dwSilenceTimeout; m_clientEngine->m_pStatsBlob->m_recStats.m_dwRPWMin = 0xFFFFFFFF; m_clientEngine->m_pStatsBlob->m_recStats.m_dwRRMin = 0xFFFFFFFF; m_clientEngine->m_pStatsBlob->m_recStats.m_dwRTSLMMin = 0xFFFFFFFF; m_clientEngine->m_pStatsBlob->m_recStats.m_dwHSMin = 0xFFFFFFFF; m_clientEngine->m_pStatsBlob->m_recStats.m_dwMLMin = 0xFFFFFFFF; m_clientEngine->m_pStatsBlob->m_recStats.m_dwCTMin = 0xFFFFFFFF; // STATSBLOCK: End
}
#undef DPF_MODNAME
#define DPF_MODNAME "CClientRecordSubSystem::BeginStats"
void CClientRecordSubSystem::BeginStats() { // STATSBLOCK: Begin
m_clientEngine->m_pStatsBlob->m_recStats.m_dwTimeStart = GetTickCount(); m_clientEngine->m_pStatsBlob->m_recStats.m_dwStartLag = m_clientEngine->m_pStatsBlob->m_recStats.m_dwTimeStart-m_clientEngine->m_pStatsBlob->m_dwTimeStart; // STATSBLOCK: end
}
#undef DPF_MODNAME
#define DPF_MODNAME "CClientRecordSubSystem::CompleteStats"
void CClientRecordSubSystem::CompleteStats() { m_clientEngine->m_pStatsBlob->m_recStats.m_dwTimeStop = GetTickCount(); }
|