1673 lines
50 KiB
C++
1673 lines
50 KiB
C++
/*==========================================================================
|
|
*
|
|
* 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();
|
|
}
|