Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

7999 lines
243 KiB

/*==========================================================================
*
* Copyright (C) 1999 Microsoft Corporation. All Rights Reserved.
*
* File: dvclientengine.cpp
* Content: Implementation of class for DirectXVoice Client
*
* History:
* Date By Reason
* ==== == ======
* 07/19/99 rodtoll Created
* 07/21/99 rodtoll Added settings confirm message to protocol
* Added storing user flags
* 07/22/99 rodtoll Updated to use new player class
* Improved concurrency protection
* Added client/server support
* 07/23/99 rodtoll Added multicast support
* Modified notify loop, now checks for multicast timeouts
* Modified playback loop to kill timed-out users
* Minor fixes to client/server support
* Other bugfixes.
* Removed valid target check in multicast and client/server
* 07/26/99 rodtoll Updated to support IDirectXVoiceNotify interface
* 07/29/99 pnewson Updated call to CInputQueue2 constructor
* 07/29/99 rodtoll Removed warnings, updated for new queue, added better
* parameter checking.
* 07/30/99 rodtoll Updated InitializeSound to use the parameters passed
* in through the sounddeviceconfig.
* 08/02/99 rodtoll Fixed bug in record volume
* 08/03/99 pnewson Cleanup of Frame and Queue classes
* 08/03/99 rodtoll Fixed calls into the transport
* 08/04/99 rodtoll Fixed up Get/SetClientConfig
* Added pointer to SoundConfig for second param of connect result
* Half duplex clients won't get record level notifications anymore
* Added connect result when error on connect
* Modified so that soundeviceconfig gets ptr to dsound/dsc devices
* Fixed bug w/sensitivity setting
* 08/05/99 rodtoll Fixed locking, was causing deadlock w/DirectPlay
* 08/10/99 rodtoll Initial support for host migration
* rodtoll No longer creates a queue for ourselves
* 08/18/99 rodtoll Fixed bug w/multicast. Added support for SPEECHBOUNCE
* message type.
* 08/25/99 rodtoll General Cleanup/Modifications to support new
* compression sub-system.
* 08/26/99 rodtoll Added copy of flags when doing SetClientConfig
* rodtoll Fixed playback thread to properly handle playback mute
* 08/27/99 rodtoll Fixed record start/stop notifications target info
* rodtoll Added playervoicestart and playervoice stop messages
* 08/30/99 rodtoll Fixed disconnect when sound init fails
* Fixed disconnect when compression type unsupported
* Fixed notification intervals
* Added timeouts to connect and disconnect processes
* Fixed bug in disconnect code
* Added re-transmission of connect request
* Fixed host migration notifications
* Fixed return code on GetCompressionTypes call.
* Updated to use new format specifications on playback
* 08/31/99 rodtoll Logic re-write to fix shutdown problems.
* - Notify thread now starts as soon as Initialize called
* and stops when object destroyed
* - Disconnect based on Disconnectconfirm or loss of
* connection signals event, notify thread then handles
* - Updated playback format to use 8Khz for playback
* 09/01/99 rodtoll Added check for valid pointers in func calls
* 09/02/99 rodtoll Added checks to handle case no local player created
* Re-activated and fixed old auto record volume code
* 09/03/99 rodtoll Re-work of playback core to support mixing to multiple buffers
* for 3d support.
* Re-worked playback to use position instead of notifications
* allows for simpler handling of high CPU and 3d support
* Implemented CreateUserBuffer/DeleteUserBuffer
* 09/04/99 rodtoll Added code to delete 3d buffers for users/groups when they
* are destroyed.
* 09/07/99 rodtoll Added support for server controlled target message
* rodtoll Added support for set/get user buffer for notarget
* rodtoll Fixed InitHalfDuplex call -- was defaulting to default device always
* 09/07/99 rodtoll Placed some guards to fix crash when Releasing when not initialized
* 09/07/99 rodtoll Updated to allow buffer management on buffer for remaining users (main buffer)
* rodtoll Updated return codes to use new errors (3d related)
* 09/08/99 rodtoll Fixed playback level checking.
* 09/10/99 rodtoll Implemented the DVCLIENTCONFIG_MUTEGLOBAL flag
* rodtoll Additional parameter validations
* 09/13/99 rodtoll Added preliminary support for new DVSOUNDDEVICECONFIG structure
* 09/14/99 rodtoll Added new DVMSGID_PLAYEROUTPUTLEVEL message
* rodtoll Added new SetNotifyMask function
* rodtoll Updated INiitalize parameters to take notification masks.
* rodtoll Implemented notification masks. (Allows user to choose which notifications to receive)
* rodtoll Added CheckShouldSendMessage
* rodtoll Added SendPlayerLevels (DVMSGID_PLAYEROUTPUTLEVEL messages handler)
* 09/15/99 rodtoll Added DVMSGID_SETTARGET message when target is set by remote server
* 09/16/99 rodtoll Updated Disconnect to alllow async and sync abortions of connects
* 09/17/99 rodtoll Fixed bug in recordthread in error handling
* 09/18/99 rodtoll Added HandleThreadError to be called when an internal thread dies.
* 09/20/99 rodtoll Updated to return SESSIONLOST message instead of DISCONNECT when session is lost
* rodtoll Functions will return DVERR_SESSIONLOST if called after session is lost.
* rodtoll Improved error checking in playback thread
* rodtoll Added more checks for memory alloc failures
* rodtoll Small bugfix in playeroutputlevel messages
* rodtoll Stricter checks on valid notify arrays
* rodtoll Added proper error checking to SetNotifyMask
* 09/27/99 rodtoll Fixed playback volume control
* rodtoll Fixed bug w/echo servers w/clients running on same dvid as host
* 09/28/99 rodtoll Fixed playervoicestart/stop notifications, now send source in the dwFrom param.
* rodtoll Double notifications of local client when host migrates fixed.
* rodtoll Added queue for notifications, notifications are added to the queue and
* then signalled by the notify thread. (Prevents problems caused by notify
* handlers taking a long time to return).
* rodtoll Added voice suppression
* 09/29/99 pnewson Major AGC overhaul
* 10/04/99 rodtoll Added usage of the DVPROTOCOL_VERSION_XXX macros
* rodtoll Added comments
* rodtoll Fixed crash which occurs if object released before initialized
* 10/05/99 rodtoll Added guards to DoDisconnect. If recording locked up on shutdown, then
* DoDisconnect would be called twice --> Crash! Fixed.
* rodtoll Reversed order of recording/playback shutdown. Shutting down playback
* before recording caused recording lockup on ESS cards.
* rodtoll Additional documentation
* 10/07/99 rodtoll Updated to work in Unicode
* rodtoll Modified notifications so connectresult should always be first
* Removed release of write locks so that connect result would be queued first.
* 10/15/99 pnewson Added config check in Connect call
* 10/18/99 rodtoll Fix: Calling initialize twice doesn't fail.
* 10/19/99 rodtoll Fix: Bug #113904 Shutdown Issues
* - Added handler for SESSIONLOST messages. Fixes shutdown lockup.
* - Changed disconnectAck event to manual reset so multiple threads can wait
* on it. Neccessary to ensure disconnect is completed before release is done
* - Changed behaviour of disconnect so that if you specify SYNC and disconnect
* is in progress, you wait for complete. Required to support disconnect in releae
* 10/25/99 rodtoll Fix: Bug #114684 - GetCaps causes lockup on shutdown
* rodtoll Fix: Bug #114223 - Debug messages being printed at error level when inappropriate
* 10/27/99 pnewson Fix: Bug #113935 - Saved AGC values should be device specific
* Fix: Bug #113936 - Wizard should reset the AGC level before loopback test
* Note: this fix adds the DVCLIENTCONFIG_AUTOVOLUMERESET flag
* 10/28/99 pnewson Bug #114176 updated DVSOUNDDEVICECONFIG struct
* 10/29/99 rodtoll Bug #113726 - Integrate Voxware Codecs, updating to use new
* pluggable codec architecture. In order to support new architecture
* all codecs creates were moved to threads where CoInitialize has been called.
* rodtoll Fixed memory leak in multicast mode caused by new architecture
* 11/04/99 pnewson Bug #114297 - Added HWND to SupervisorCheckAudioSetup call
* 11/12/99 rodtoll Updated to use new playback and record classes and remove
* old playback/record system (Includes waveIN/waveOut support)
* rodtoll Updated to support new recording thread
* rodtoll Added new echo suppression code
* 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.
* 11/17/99 rodtoll Fix: Bug #115538 - dwSize members of > sizeof struct were accepted
* rodtoll Fix: Bug #115827 - Calling SetNotifyMask w/no callback should fail
* rodtoll Fix: Bug #117442 - Calling Disconnect with invalid flags doesn't return DVERR_INVALIDFLAGS
* rodtoll Fix: Bug #117447 - GetTransmitTarget has problems
* rodtoll Fix: Bug #117177 - Calling Connect w/o voice session never returns
* 11/18/99 rodtoll Updated to control echo cancellation switching code by define.
* 11/22/99 rodtoll Fixed problem caused by switching on echo cancellation while talking
* 11/22/99 rodtoll Fixed Initialize() would fail incorrectly
* 11/23/99 rodtoll Updated Initialize/SetNotifyMask so error checking behaviour is consistant
* 11/24/99 rodtoll Adjusted Set/GetTransmit Target so locks are released before calling into dplay
* 11/30/99 pnewson Reworked default device mapping code
* Adjusted some timing issues to make single stepping connect possible
* 12/01/99 rodtoll Bug #121815 - Recording/playback may contain static. Updated to call functions
* to set conversion quality setting to high.
* rodtoll Bug #115783 - Always adjusts volume for default device. Fixed for Win2k, Win9x w/DX7
* Systems w/DX5 or none will use waveIN/waveOUT and will default to default device.
* 12/02/99 rodtoll Bug #115783 - Will now use waveIN/waveOut object corresponding to specified GUID
* on DX3 systems.
* 12/06/99 rodtoll Bumped playback/record threads to time critical priority
* 12/16/99 rodtoll Bug #117405 - 3D Sound APIs misleading - 3d sound apis renamed
* The Delete3DSoundBuffer was re-worked to match the create
* rodtoll Bug #122629 - Host migration broken in unusual configurations
* Implemented new host migration scheme.
* rodtoll Bug #121054 - DirectX 7.1 changes must be incorporated
* rodtoll Implemented new DVPROTOCOLMSG_PLAYERLIST message to handle player table message.
* rodtoll As part of new host migration, implemented proper handling of connection
* rejected message (was broken, exposed by new host migration).
* rodtoll Updated Disconnect to handle inability to contact server properly which
* was resulting in an error message (when it should disconnect anyhow).
* rodtoll Removed voice suppression
* 01/10/00 pnewson AGC and VA tuning
* 01/14/2000 rodtoll Updated for new speech packet types / packet handling
* rodtoll Updated for new Get/SetTransmitTargets functions
* rodtoll Added support for multiple targets
* rodtoll Added use of fixed pool manager to manage memory for
* notifications.
* rodtoll Updated notifications to support messages with memory.
* rodtoll Updated message handler calls to use new format
* rodtoll Updated all notifications to use new message structures
* rodtoll Updated Connect/Disconnect so that when DVFLAGS_SYNCH is
* specified no completion messages will be sent.
* rodtoll Added new API call GetSoundDeviceConfig
* 01/21/2000 pnewson Changes in support of revised wizard UI
* Allow concurrent AGC and user controlled volume
* 01/24/2000 rodtoll Bug #129427: Calling Destroy3DSoundBuffer for player who has
* already disconnected resulted in an incorrect DVERR_NOTBUFFERED
* error code.
* 01/27/2000 rodtoll Bug #129934 - Update Create3DSoundBuffer to take DSBUFFERDESC
* 01/28/2000 rodtoll Bug #130465: Record Mute/Unmute must call YieldFocus() / ClaimFocus()
* 02/01/2000 rodtoll Disable capture focus features - Bug #129457
* 02/08/2000 rodtoll Bug #131496 - Selecting DVTHRESHOLD_DEFAULT results in voice
* never being detected
* 02/15/2000 rodtoll Fixed Connect so mapping GUIDs doesn't stomp user structure
* 02/17/2000 rodtoll Bug #133691 - Choppy audio - queue was not adapting
* Added instrumentation
* 03/28/2000 rodtoll Re-wrote nametable handling and locking -- more scalable
* rodtoll Fixed pool for players
* rodtoll Bilink of "active players" and "players to notify" allows for greater
* concurrency (playback and notify threads don't need to lock entire
* nametable while running.
* 03/29/2000 rodtoll Bug #30957 - Made conversion quality slider setting optional -- new flag -- DVSOUNDCONFIG_SETCONVERSIONQUALITY
* rodtoll Incorporated experimental playback handling w/lower priority and more frequent wakeup
* rodtoll Instead of calling ConfirmValidEntity now checks nametable
* 04/07/2000 rodtoll Bug #32179 - Prevent registration of > 1 interface
* rodtoll Updated to use no copy sends, so handles pooling frames to be sent, proper
* pulling of frames from pools and returns.
* 04/19/2000 rodtoll Re-enabled capture focus behaviour / found not working on WDM, re-disabled.
* rodtoll Bug #31106 - Handle sound devices w/no recording volume
* Set DVSOUNDCONFIG_NORECVOLAVAILABLE flag on DVSOUNDDEVICECONFIG and do not
* perform any volume sets when this flag is present
* 04/20/2000 rodtoll Bug #31478 - Lockup in shutdown on client who has become new host -- ref count issue
* 04/24/2000 rodtoll Bug #33228 - Compile error reported by davidkl
* 04/27/2000 rodtoll Fix for host migration crash turned out to be sample bug, restoring.
* rodtoll Fix for crash on Connect failed
* 05/11/2000 rodtoll Bug #34852 Voice connection crashes after being in a session for a couple of minutes
* 05/17/2000 rodtoll Bug #35110 Simultaneous playback of 2 voices results in distorted playback
* 05/30/2000 rodtoll Bug #35476 Access violation in DPVOICE.DLL on host after client left
* Host migration code was being fired because of DirectPlay8's nametable unwinding funcs.
* 05/31/2000 rodtoll Bug #35860 - Fix VC6 compile errors for instrumented builds
* rodtoll Bug #35794 - Setting targets to none results in leak.
* 06/02/2000 rodtoll Moved host migration so it is keyed off voice message and transport messages.
* More reliable this way.
* 06/21/2000 rodtoll Bug #35767 - Implement ability for Dsound effects processing if dpvoice buffers
* Updated Connect and Create3DSoundBuffer to take buffers instead of descriptions
* rodtoll Bug #36820 - Host migrates to wrong client when server is shut down before host's client disconnects
* Caused because client attempts to register new server when there is one already
* rodtoll Bug #37045 - Race conditions prevent acknowledgement of new host
* Added send when new host is elected of settingsconfirm message
* 06/27/2000 rodtoll Fixed window where outstanding sends being returned after we have deregistered
* Voice now waits for all outstanding voice sends to complete before shutting down
* rodtoll Added COM abstraction
* 06/28/2000 rodtoll Prefix Bug #38022
* 07/01/2000 rodtoll Bug #38280 - DVMSGID_DELETEVOICEPLAYER messages are being sent in non-peer to peer sessions
* Nametable will now only unravel with messages if session is peer to peer.
* rodtoll Bug #38316 - HOST MIGRATION - Player fails to get HOST_MIGRATED message
* 07/09/2000 rodtoll Added signature bytes
* 07/12/2000 rodtoll Bug #39117 - Access violation while running VoicePosition. Several issues:
* - Allow Destroy3DBuffer during disconnect
* - Move nametable cleanup to before freesoundbufferlist
* - Fixed code so always remove from list on DeleteSoundTarget
* - Removed unneeded logic
* 07/12/2000 rodtoll Bug #31468 - Add diagnostic spew to logfile to show what is failing the HW Wizard
* rodtoll Bug #32841 - Renable support for capture focus
* 07/22/2000 rodtoll Bug #40284 - Initialize() and SetNotifyMask() should return invalidparam instead of invalidpointer
* rodtoll Bug #40296, 38858 - Crashes due to shutdown race condition
* Now ensures that all threads from transport have left and that
* all notificatinos have been processed before shutdown is complete.
* rodtoll Bug #39586 - Trap 14 in DPVVOX.DLL during session of voicegroup, adding guards for overwrites
* 07/26/2000 rodtoll Bug #40676 - Forwarding server is broken
* 07/31/2000 rodtoll Bug #40470 - SetClientConfig() with invalid flags returns INVALIDPARAM
* 08/03/2000 rodtoll Bug #41457 - DPVOICE: need way to discover which specific dsound call failed when returning DVERR_SOUNDINITFAILURE
* 08/08/2000 rodtoll Was missing a DNLeaveCriticalSection
* 08/09/2000 rodtoll Bug #41936 - No voice session message instead of compression not supported
* 08/21/2000 rodtoll Bug #41475 - DPVOICE: Lockup during shutdown when deleteplayer messages received
* 08/22/2000 rodtoll Bug #43095 - DPVOICE: DVMSGID_GAINFOCUS and DVMSGID_LOSTFOCUS are not passing NULL message parameters
* 08/28/2000 masonb Voice Merge: DNet FPOOLs use DNCRITICAL_SECTION, modified m_pBufferDescPool usage
* 08/29/2000 rodtoll Bug #43668 - DPVOICE: Asserts when exiting DPVOICE session
* 08/31/2000 rodtoll Bug #43804 - DVOICE: dwSensitivity structure member is confusing - should be dwThreshold
* 08/31/2000 rodtoll Whistler Bug #171841 - Prefix Bug
* 09/01/2000 masonb Modified PlaybackThread, RecordThread, and NotifyThread to call _endthread to clean up thread handles
* 09/14/2000 rodtoll Bug #45001 - DVOICE: AV if client has targetted > 10 players
* 09/26/2000 rodtoll Bug #45541 - DPVOICE: Client gets DVERR_TIMEOUT message when disconnecting
* 09/28/2000 rodtoll Fix Again: Bug #45541 - DPVOICE: Client gets DVERR_TIMEOUT message when disconnecting (Server always confirms disconnect)
* 10/05/2000 rodtoll Bug #46541 - DPVOICE: A/V linking to dpvoice.lib could cause application to fail init and crash
* 11/16/2000 rodtoll Bug #40587 - DPVOICE: Mixing server needs to use multi-processors
* 01/22/2001 rodtoll WINBUG #288437 - IA64 Pointer misalignment due to wire packets
* 01/25/2001 rodtoll WINBUG #293197 - DPVOICE: Stress applications cannot tell the difference between out of memory/invalid device/other errors
* 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/04/2001 rodtoll WINBUG #343428 - DPVOICE: Voice wizard's playback is very choppy.
* rodtoll WINBUG #356124 - STRESS: DPVLPY7 broke when Initialize() failed due to being out of memory.
* 04/02/2001 simonpow Bug #354859 Fixes for problems spotted by PREfast (hresult casting to bool and local variable hiding)
* 04/06/2001 kareemc Added Voice Defense
* 04/12/2001 kareemc WINBUG #360971 - Wizard Memory Leaks
* 04/09/2001 rodtoll WINBUG #363804 DPVOICE: Race condition in shutdown causes disconnect to timeout if session is being shutdown concurrently.
* 04/11/2001 rodtoll WINBUG #221494 DPVOICE: Capped the # of queued recording events to prevent multiple wakeups resulting in false lockup
* detection
* 04/12/2001 simonpow WINBUG #322454 Removed early unlock in RemovePlayer method and added
* extra return code checking in DoConnectResponse
* 04/21/2001 rodtoll MANBUG #50058 DPVOICE: VoicePosition: No sound for couple of seconds when position bars are moved
* - Added StartMix() call when secondary buffers are created
* - Added extra bit of debug spew, removed dead code
* 04/21/2001 rodtoll (for Simonpow)
* WINBUG #322454 DPVOICE: [STRESS] Connect() appears to be suceeding (returns S_OK) but connection is not established.
* 09/05/2001 simonpow Bug #463972. Added constuct/destruct methods to initialisation
* calls on m_fpPlayers for CVoicePlayer
* 11/12/2001 simonpow Bug #482302 Missing a DNLeaveCriticalSection in Cleanup
* 02/25/2002 rodtoll WINBUG #550017 - SECURITY: DPVOICE: Client could inject speech into remove clients output
* - Confirm voice messages in forwarding sessions come from the server.
* rodtoll WINBUG #550009 - SECURITY: DPVOICE: Potential corruption of voice client state
* - Harden receive pathways: i.e. confirm message sources from server, confirm message makes
* sense w.r.t session settings, tighten validation, prevent duplicate disconnects from executing.
* rodtoll WINBUG #550124 - SECURITY: DPVOICE: Shared memory region with NULL DACL
* - Pull statistics exporting code
* 03/01/2002 simonpow Fixed FreeBuffers to ensure it doesn't deinitialise any fixed
* pools that haven't been initialised
* 05/01/2002 simonpow Ensured GUID passed to DVCDB_GetCompressionInfo is aligned
* in HandleConnectAccept
* 06/13/2002 simonpow BUG #59944 Switched over to using Threadpool based timers rather than multimedia
***************************************************************************/
#include "dxvoicepch.h"
extern HRESULT DVS_Create(LPDIRECTVOICESERVEROBJECT *piDVS);
// Use high priority for playback / record threads
#define __CORE_THREAD_PRIORITY_HIGH
// Disables the sound system
//#define __DISABLE_SOUND
// Forces full duplex mode
//#define __FORCEFULLDUPLEX
// # of ms of inactivity before a multicast user is considered to have
// timed-out.
#define DV_MULTICAST_USERTIMEOUT_PERIOD 300000
// # of ms of inactivity before an incoming audio stream is considered to
// have stopped. Used to determine when to send PLAYERVOICESTOP
// message.
#define PLAYBACK_RECEIVESTOP_TIMEOUT 500
// # of ms the notify thread sleeps without notification to wakeup
// and perform house cleaning.
#define DV_CLIENT_NOTIFYWAKEUP_TIMEOUT 100
// # of ms before a connect request is considered to have been lost
#define DV_CLIENT_CONNECT_RETRY_TIMEOUT 1250
// # of ms before we should timeout a connect request completely
#define DV_CLIENT_CONNECT_TIMEOUT 30000
// Maximum count notification semaphores can have
#define DVCLIENT_NOTIFY_MAXSEMCOUNT 0x0FFFFFFF
//// TODO: Needs tuning.
// # of ms to wait for disconnect reply from server before timing out.
#define DV_CLIENT_DISCONNECT_TIMEOUT 10000
#define DV_CLIENT_SRCQUALITY_INVALID ((DIRECTSOUNDMIXER_SRCQUALITY) 0xFFFFFFFF)
#define CLIENT_POOLS_NUM 3
#define CLIENT_POOLS_SIZE_MESSAGE (sizeof(DVPROTOCOLMSG_FULLMESSAGE))
#define CLIENT_POOLS_SIZE_PLAYERLIST DVPROTOCOL_PLAYERLIST_MAXSIZE
// DV_CLIENT_EMULATED_LEAD_ADJUST
//
// # of buffer's worth of lead ahead of read pointer allowable to write
// in playback buffer when buffer is emulated (additional, not total)
#define DV_CLIENT_EMULATED_LEAD_ADJUST 2
// DV_CLIENT_BASE_LEAD_MAX
//
// Maximum # of buffers lead to allow
#define DV_CLIENT_BASE_LEAD_MAX 2
// MixingWakeupProc
//
// This function is called by the DvTimer timer used by this
// class each time the timer goes off. The function signals
// a semaphore provided by the creator of the timer.
//
// Parameters:
// DWORD param - A recast pointer to a HANDLE
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::MixingWakeupProc"
void CDirectVoiceClientEngine::MixingWakeupProc( void * pvUserData )
{
TimerHandlerParam *pParam = (TimerHandlerParam *) pvUserData;
SetEvent( pParam->hPlaybackTimerEvent );
SetEvent( pParam->hRecordTimerEvent );
DNEnterCriticalSection( &pParam->csPlayCount );
pParam->lPlaybackCount++;
DNLeaveCriticalSection( &pParam->csPlayCount );
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::CDirectVoiceClientEngine"
//
// Constructor
//
// Initializes object to uninitialized state. Must call Initialize succesfully before
// the object can be used (except GetCompressionTypes which can be called at any time).
//
CDirectVoiceClientEngine::CDirectVoiceClientEngine( DIRECTVOICECLIENTOBJECT *lpObject
): m_dwSignature(VSIG_CLIENTENGINE),
m_lpFramePool(NULL),
m_lpObject(lpObject),
m_lpSessionTransport(NULL),
m_lpUserContext(NULL),
m_dvidServer(0),
m_pTimer(NULL),
m_bLastPeak(0),
m_bLastTransmitted(FALSE),
m_bMsgNum(0),
m_bSeqNum(0),
m_dwActiveCount(0),
m_dwLastConnectSent(0),
m_audioPlaybackBuffer(NULL),
m_audioRecordDevice(NULL),
m_audioPlaybackDevice(NULL),
m_audioRecordBuffer(NULL),
m_hRecordDone(NULL),
m_hRecordTerminate(NULL),
m_hPlaybackDone(NULL),
m_hPlaybackTerminate(NULL),
m_hConnectAck(NULL),
m_dwCurrentState(DVCSTATE_NOTINITIALIZED),
m_hrConnectResult(DVERR_GENERIC),
m_hrOriginalConnectResult(DVERR_GENERIC),
m_hrDisconnectResult(DV_OK),
m_hDisconnectAck(NULL),
m_hNotifyDone( NULL ),
m_hNotifyTerminate( NULL ),
m_hNotifyChange( NULL ),
m_bLastPlaybackPeak( 0 ),
m_lpdvServerMigrated(NULL),
m_hNotifyDisconnect(NULL),
m_hNotifyConnect(NULL),
m_pFramePool(NULL),
m_lpstGeneralBuffer(NULL),
m_lpstBufferList(NULL),
m_lpdwMessageElements(NULL),
m_dwNumMessageElements(0),
m_fSessionLost(FALSE),
m_fLocalPlayerNotify(FALSE),
m_lpNotifyList(NULL),
m_hNewNotifyElement(NULL),
m_dwHostOrderID(DVPROTOCOL_HOSTORDER_INVALID),
m_pdvidTargets(NULL),
m_dwNumTargets(0),
m_dwTargetVersion(0),
m_fConnectAsync(false),
m_fDisconnectAsync(false),
m_dwOriginalRecordQuality(DV_CLIENT_SRCQUALITY_INVALID),
m_dwOriginalPlayQuality(DV_CLIENT_SRCQUALITY_INVALID),
m_dwPlayActiveCount(0),
m_fLocalPlayerAvailable(FALSE),
m_fNotifyQueueEnabled(FALSE),
m_hRecordThreadHandle(NULL),
m_hPlaybackThreadHandle(NULL),
m_dwMigrateHostOrderID(DVPROTOCOL_HOSTORDER_INVALID),
m_pStatsBlob(NULL),
m_fCritSecInited(FALSE),
m_iPlayerListReceived(-1),
m_fDisconnecting(FALSE)
{
m_dvSoundDeviceConfig.lpdsCaptureDevice = NULL;
m_dvSoundDeviceConfig.lpdsPlaybackDevice = NULL;
memset( &m_dvCaps, 0x00, sizeof( DVCAPS ) );
m_dvCaps.dwSize = sizeof( DVCAPS );
m_dvCaps.dwFlags = 0;
ClientStats_Reset();
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::InitClass"
BOOL CDirectVoiceClientEngine::InitClass( )
{
if (!DNInitializeCriticalSection( &m_csNotifyQueueLock ))
{
return FALSE;
}
if (!DNInitializeCriticalSection( &m_lockPlaybackMode ))
{
DNDeleteCriticalSection( &m_csNotifyQueueLock );
return FALSE;
}
if (!DNInitializeCriticalSection( &m_csTargetLock ))
{
DNDeleteCriticalSection( &m_lockPlaybackMode );
DNDeleteCriticalSection( &m_csNotifyQueueLock );
return FALSE;
}
if (!DNInitializeCriticalSection( &m_csCleanupProtect ))
{
DNDeleteCriticalSection( &m_csTargetLock );
DNDeleteCriticalSection( &m_lockPlaybackMode );
DNDeleteCriticalSection( &m_csNotifyQueueLock );
return FALSE;
}
if (!DNInitializeCriticalSection( &m_csBufferLock ))
{
DNDeleteCriticalSection( &m_csCleanupProtect );
DNDeleteCriticalSection( &m_csTargetLock );
DNDeleteCriticalSection( &m_lockPlaybackMode );
DNDeleteCriticalSection( &m_csNotifyQueueLock );
return FALSE;
}
if (!DNInitializeCriticalSection( &m_csPlayAddList ))
{
DNDeleteCriticalSection( &m_csBufferLock );
DNDeleteCriticalSection( &m_csCleanupProtect );
DNDeleteCriticalSection( &m_csTargetLock );
DNDeleteCriticalSection( &m_lockPlaybackMode );
DNDeleteCriticalSection( &m_csNotifyQueueLock );
return FALSE;
}
if (!DNInitializeCriticalSection( &m_csNotifyAddList ))
{
DNDeleteCriticalSection( &m_csPlayAddList );
DNDeleteCriticalSection( &m_csBufferLock );
DNDeleteCriticalSection( &m_csCleanupProtect );
DNDeleteCriticalSection( &m_csTargetLock );
DNDeleteCriticalSection( &m_lockPlaybackMode );
DNDeleteCriticalSection( &m_csNotifyQueueLock );
return FALSE;
}
if (!DNInitializeCriticalSection( &m_csTransmitBufferLock ))
{
DNDeleteCriticalSection( &m_csNotifyAddList );
DNDeleteCriticalSection( &m_csPlayAddList );
DNDeleteCriticalSection( &m_csBufferLock );
DNDeleteCriticalSection( &m_csCleanupProtect );
DNDeleteCriticalSection( &m_csTargetLock );
DNDeleteCriticalSection( &m_lockPlaybackMode );
DNDeleteCriticalSection( &m_csNotifyQueueLock );
return FALSE;
}
if (!DNInitializeCriticalSection( &m_thTimerInfo.csPlayCount ))
{
DNDeleteCriticalSection( &m_csTransmitBufferLock );
DNDeleteCriticalSection( &m_csNotifyAddList );
DNDeleteCriticalSection( &m_csPlayAddList );
DNDeleteCriticalSection( &m_csBufferLock );
DNDeleteCriticalSection( &m_csCleanupProtect );
DNDeleteCriticalSection( &m_csTargetLock );
DNDeleteCriticalSection( &m_lockPlaybackMode );
DNDeleteCriticalSection( &m_csNotifyQueueLock );
return FALSE;
}
if (!DNInitializeCriticalSection( &m_csClassLock ))
{
DNDeleteCriticalSection( &m_thTimerInfo.csPlayCount );
DNDeleteCriticalSection( &m_csTransmitBufferLock );
DNDeleteCriticalSection( &m_csNotifyAddList );
DNDeleteCriticalSection( &m_csPlayAddList );
DNDeleteCriticalSection( &m_csBufferLock );
DNDeleteCriticalSection( &m_csCleanupProtect );
DNDeleteCriticalSection( &m_csTargetLock );
DNDeleteCriticalSection( &m_lockPlaybackMode );
DNDeleteCriticalSection( &m_csNotifyQueueLock );
return FALSE;
}
if (!DNInitializeCriticalSection( &m_csNotifyLock ))
{
DNDeleteCriticalSection( &m_csClassLock );
DNDeleteCriticalSection( &m_thTimerInfo.csPlayCount );
DNDeleteCriticalSection( &m_csTransmitBufferLock );
DNDeleteCriticalSection( &m_csNotifyAddList );
DNDeleteCriticalSection( &m_csPlayAddList );
DNDeleteCriticalSection( &m_csBufferLock );
DNDeleteCriticalSection( &m_csCleanupProtect );
DNDeleteCriticalSection( &m_csTargetLock );
DNDeleteCriticalSection( &m_lockPlaybackMode );
DNDeleteCriticalSection( &m_csNotifyQueueLock );
return FALSE;
}
m_fCritSecInited = TRUE;
return TRUE;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::~CDirectVoiceClientEngine"
// Destructor
//
// This function requires a write lock to complete.
//
// If the object is connected to a session, it will be disconnected
// by this function.
//
// Releases the resources associated with the object and stops the
// notifythread.
//
// Locks Required:
// - Class Write Lock
//
// Called By:
// DVC_Release when reference count reaches 0.
//
CDirectVoiceClientEngine::~CDirectVoiceClientEngine()
{
CDVCSLock guardLock(&m_csClassLock);
guardLock.Lock();
if( m_dwCurrentState != DVCSTATE_IDLE &&
m_dwCurrentState != DVCSTATE_NOTINITIALIZED )
{
Cleanup();
}
if( m_hNotifyDone != NULL )
{
SetEvent( m_hNotifyTerminate );
WaitForSingleObject( m_hNotifyDone, INFINITE );
CloseHandle( m_hNotifyDone );
CloseHandle( m_hNotifyTerminate );
CloseHandle( m_hNotifyChange );
CloseHandle( m_hNotifyDisconnect );
CloseHandle( m_hNotifyConnect );
m_hNotifyDone = NULL;
m_hNotifyTerminate = NULL;
m_hNotifyChange = NULL;
m_hNotifyDisconnect = NULL;
m_hNotifyConnect = NULL;
}
if( m_hConnectAck != NULL )
CloseHandle( m_hConnectAck );
if( m_hDisconnectAck != NULL )
CloseHandle( m_hDisconnectAck );
if( m_lpdvServerMigrated != NULL )
{
m_lpdvServerMigrated->Release();
m_lpdvServerMigrated = NULL;
}
if( m_lpdwMessageElements != NULL )
delete [] m_lpdwMessageElements;
guardLock.Unlock();
NotifyQueue_Free();
if (m_fCritSecInited)
{
DNDeleteCriticalSection( &m_lockPlaybackMode );
DNDeleteCriticalSection( &m_csTargetLock );
DNDeleteCriticalSection( &m_csCleanupProtect );
DNDeleteCriticalSection( &m_csBufferLock );
DNDeleteCriticalSection( &m_thTimerInfo.csPlayCount );
DNDeleteCriticalSection( &m_csPlayAddList );
DNDeleteCriticalSection( &m_csNotifyAddList );
DNDeleteCriticalSection( &m_csTransmitBufferLock );
DNDeleteCriticalSection( &m_csClassLock );
DNDeleteCriticalSection( &m_csNotifyQueueLock );
DNDeleteCriticalSection( &m_csNotifyLock );
}
if( m_pdvidTargets != NULL )
{
delete [] m_pdvidTargets;
}
m_dwSignature = VSIG_CLIENTENGINE_FREE;
}
// InternalSetNotifyMask
//
// Sets the list of valid notifiers for this object.
//
// Locks Needed:
// - ReadLock to check status and then releases it.
// - m_csNotifyLock to update notification list
//
// Called By:
// DVC_SetNotifyMask
//
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::InternalSetNotifyMask"
HRESULT CDirectVoiceClientEngine::InternalSetNotifyMask( LPDWORD lpdwMessages, DWORD dwNumElements )
{
BFCSingleLock slLock( &m_csNotifyLock );
slLock.Lock();
// Delete previous elements
if( m_lpdwMessageElements != NULL )
{
delete [] m_lpdwMessageElements;
}
m_dwNumMessageElements = dwNumElements;
// Make copies of the message elements into our own message array.
if( dwNumElements > 0 )
{
m_lpdwMessageElements = new DWORD[dwNumElements];
if( m_lpdwMessageElements == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Initialize: Error allocating memory" );
return DVERR_OUTOFMEMORY;
}
memcpy( m_lpdwMessageElements, lpdwMessages, sizeof(DWORD)*dwNumElements );
}
else
{
m_lpdwMessageElements = NULL;
}
return DV_OK;
}
// SetNotifyMask
//
// Sets the list of valid notifiers for this object.
//
// Locks Needed:
// - ReadLock to check status and then releases it.
// - m_csNotifyLock to update notification list
//
// Called By:
// DVC_SetNotifyMask
//
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::SetNotifyMask"
HRESULT CDirectVoiceClientEngine::SetNotifyMask( LPDWORD lpdwMessages, DWORD dwNumElements )
{
HRESULT hr;
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Enter" );
hr = DV_ValidMessageArray( lpdwMessages, dwNumElements, FALSE );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "ValidMessageArray Failed 0x%x", hr );
return hr;
}
DPFX(DPFPREP, DVF_APIPARAM, "Message IDs=%d", dwNumElements );
if( lpdwMessages != NULL )
{
for( DWORD dwIndex = 0; dwIndex < dwNumElements; dwIndex++ )
{
DPFX(DPFPREP, DVF_APIPARAM, "MessageIDs[%d] = %d", dwIndex, lpdwMessages[dwIndex] );
}
}
CDVCSLock guardLock(&m_csClassLock);
guardLock.Lock();
if( m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Not initialized" );
return DVERR_NOTINITIALIZED;
}
guardLock.Unlock();
BFCSingleLock slLock( &m_csNotifyLock );
slLock.Lock();
if( m_lpMessageHandler == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Cannot specify message mask there is no callback function" );
return DVERR_NOCALLBACK;
}
hr = InternalSetNotifyMask( lpdwMessages, dwNumElements );
DPFX(DPFPREP, DVF_APIPARAM, "Returning hr=0x%x", hr );
return DV_OK;
}
// Initialize
//
// Initializes this object into a state where it can be used to Connect to a session. Sets the
// notification function, notification mask and the transport object that will be used.
//
// Starts the notification thread.
//
// Locks Required:
// - Class Write Lock
//
// Called By:
// DV_Initialize
//
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::Initialize"
HRESULT CDirectVoiceClientEngine::Initialize( CDirectVoiceTransport *lpTransport, LPDVMESSAGEHANDLER lpdvHandler, LPVOID lpUserContext, LPDWORD lpdwMessages, DWORD dwNumElements )
{
HRESULT hr;
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Enter" );
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
DPFX(DPFPREP, DVF_APIPARAM, "Param: lpTransport = 0x%p lpdvHandler = 0x%p lpUserContext = 0x%p dwNumElements = %d", lpTransport, lpdvHandler, lpUserContext, dwNumElements );
if( lpTransport == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid transport" );
return DVERR_INVALIDPOINTER;
}
hr = DV_ValidMessageArray( lpdwMessages, dwNumElements, FALSE );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "ValidMessageArray Failed hr = 0x%x", hr );
return hr;
}
DPFX(DPFPREP, DVF_APIPARAM, "Message IDs=%d", dwNumElements );
if( lpdwMessages != NULL )
{
for( DWORD dwIndex = 0; dwIndex < dwNumElements; dwIndex++ )
{
DPFX(DPFPREP, DVF_APIPARAM, "MessageIDs[%d] = %d", dwIndex, lpdwMessages[dwIndex] );
}
}
HANDLE hThread;
// Wait for a write lock on the object
CDVCSLock guardLock(&m_csClassLock);
guardLock.Lock();
if( m_dwCurrentState != DVCSTATE_NOTINITIALIZED )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Already Initialized" );
return DVERR_INITIALIZED;
}
if( lpdvHandler == NULL && lpdwMessages != NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Cannot specify message mask there is no callback function" );
return DVERR_NOCALLBACK;
}
m_dwLastConnectSent = 0;
m_dwSynchBegin = 0;
SetCurrentState( DVCSTATE_IDLE );
BFCSingleLock slLock( &m_csNotifyLock );
slLock.Lock();
m_lpMessageHandler = lpdvHandler;
hr = InternalSetNotifyMask( lpdwMessages, dwNumElements );
if( FAILED( hr ) )
{
SetCurrentState( DVCSTATE_NOTINITIALIZED );
DPFX(DPFPREP, DVF_ERRORLEVEL, "SetNotifyMask Failed hr=0x%x", hr );
return hr;
}
m_lpSessionTransport = lpTransport;
m_lpUserContext = lpUserContext;
m_dvidLocal = 0;
m_dwActiveCount = 0;
m_thTimerInfo.hPlaybackTimerEvent = NULL;
m_thTimerInfo.lPlaybackCount = 0;
m_thTimerInfo.hRecordTimerEvent = NULL;
m_hConnectAck = CreateEvent( NULL, FALSE, FALSE, NULL );
m_hDisconnectAck = CreateEvent( NULL, TRUE, FALSE, NULL );
m_hNotifyDone = CreateEvent( NULL, FALSE, FALSE, NULL );
m_hNotifyTerminate = CreateEvent( NULL, FALSE, FALSE, NULL );
m_hNotifyChange = CreateEvent( NULL, FALSE, FALSE, NULL );
m_hNotifyDisconnect = CreateEvent( NULL, FALSE, FALSE, NULL );
m_hNotifyConnect = CreateEvent( NULL, FALSE, FALSE, NULL );
if( m_hConnectAck == NULL ||
m_hDisconnectAck == NULL ||
m_hNotifyTerminate == NULL ||
m_hNotifyChange == NULL ||
m_hNotifyDisconnect == NULL ||
m_hNotifyConnect == NULL ||
m_hNotifyDone==NULL)
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to create required events" );
goto ERROR_EXIT_INIT;
}
if (FAILED(NotifyQueue_Init()))
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to init notify queue" );
goto ERROR_EXIT_INIT;
}
hThread = (HANDLE) _beginthread( NotifyThread, 0, this );
if( hThread == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to create watcher thread" );
goto ERROR_EXIT_INIT;
}
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
DPFX(DPFPREP, DVF_INFOLEVEL, "Notify Thread Started: 0x%p", hThread );
guardLock.Unlock();
DPFX(DPFPREP, DVF_APIPARAM, "Returning DV_OK" );
return DV_OK;
ERROR_EXIT_INIT:
if( m_hConnectAck != NULL )
{
CloseHandle( m_hConnectAck );
m_hConnectAck = NULL;
}
if( m_hDisconnectAck != NULL )
{
CloseHandle( m_hDisconnectAck );
m_hDisconnectAck = NULL;
}
if( m_hNotifyTerminate != NULL )
{
CloseHandle( m_hNotifyTerminate );
m_hNotifyTerminate = NULL;
}
if( m_hNotifyChange != NULL )
{
CloseHandle( m_hNotifyChange );
m_hNotifyChange = NULL;
}
if( m_hNotifyDisconnect != NULL )
{
CloseHandle( m_hNotifyDisconnect );
m_hNotifyDisconnect = NULL;
}
if( m_hNotifyConnect != NULL )
{
CloseHandle( m_hNotifyConnect );
m_hNotifyConnect = NULL;
}
DPFX(DPFPREP, DVF_ERRORLEVEL, "Returning DVERR_GENERIC" );
return DVERR_GENERIC;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::Connect"
// Connect
//
// Implements the IDirectXVoiceClient::Connect function.
//
// Locks Required:
// - Write Lock
//
// Called By:
// DVC_Connect
//
HRESULT CDirectVoiceClientEngine::Connect( LPDVSOUNDDEVICECONFIG lpSoundDeviceConfig, LPDVCLIENTCONFIG lpClientConfig, DWORD dwFlags )
{
HRESULT hr;
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Enter" );
hr = DV_ValidClientConfig( lpClientConfig );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid Client Config hr=0x%x", hr );
return hr;
}
hr = DV_ValidSoundDeviceConfig( lpSoundDeviceConfig, s_lpwfxPlaybackFormat );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid Sound Device Config hr=0x%x", hr );
return hr;
}
if( dwFlags & ~(DVFLAGS_SYNC|DVFLAGS_NOQUERY) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid flags specified" );
return DVERR_INVALIDFLAGS;
}
DV_DUMP_SDC( lpSoundDeviceConfig );
DV_DUMP_CC( lpClientConfig );
CDVCSLock guardLock(&m_csClassLock);
guardLock.Lock();
if( m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Object not initialized" );
return DVERR_NOTINITIALIZED;
}
if( m_dwCurrentState == DVCSTATE_CONNECTING ||
m_dwCurrentState == DVCSTATE_DISCONNECTING )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Already connecting or disconnecting" );
return DVERR_ALREADYPENDING;
}
if( m_dwCurrentState == DVCSTATE_CONNECTED )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Already connected" );
return DVERR_CONNECTED;
}
// Copy over the parameters
memcpy( &m_dvSoundDeviceConfig, lpSoundDeviceConfig, sizeof( DVSOUNDDEVICECONFIG ) );
// map the devices
GUID guidTemp;
hr = DV_MapCaptureDevice(&(m_dvSoundDeviceConfig.guidCaptureDevice), &guidTemp);
if (FAILED(hr))
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "DV_MapCaptureDevice failed, code: %i", hr);
return hr;
}
m_dvSoundDeviceConfig.guidCaptureDevice = guidTemp;
hr = DV_MapPlaybackDevice(&(m_dvSoundDeviceConfig.guidPlaybackDevice), &guidTemp);
if (FAILED(hr))
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "DV_MapPlaybackDevice failed, code: %i", hr);
return hr;
}
m_dvSoundDeviceConfig.guidPlaybackDevice = guidTemp;
// Check to ensure setup has been run on these devices
// but only if the NOQUERY flag has not been set.
if (!(dwFlags & DVFLAGS_NOQUERY))
{
hr = SupervisorCheckAudioSetup(
&(m_dvSoundDeviceConfig.guidPlaybackDevice),
&(m_dvSoundDeviceConfig.guidCaptureDevice),
NULL,
DVFLAGS_QUERYONLY);
switch (hr)
{
case DV_FULLDUPLEX:
// great - carry on.
DPFX(DPFPREP, DVF_INFOLEVEL, "Devices have been tested - full duplex ok");
break;
case DV_HALFDUPLEX:
// force on the half duplex flag.
DPFX(DPFPREP, DVF_INFOLEVEL, "Devices have been tested - half duplex only");
m_dvSoundDeviceConfig.dwFlags |= DVSOUNDCONFIG_HALFDUPLEX;
break;
case DVERR_SOUNDINITFAILURE:
// The devices were tested, and failed miserably.
DPFX(DPFPREP, DVF_INFOLEVEL, "Devices have been tested - total failure");
return DVERR_SOUNDINITFAILURE;
break;
case DVERR_RUNSETUP:
// return the run setup code
DPFX(DPFPREP, DVF_ERRORLEVEL, "Devices have not been tested");
return DVERR_RUNSETUP;
default:
// unexpected return code - this is a real error - propogate it back up
DPFX(DPFPREP, DVF_ERRORLEVEL, "SupervisorCheckAudioSetup failed, code: %i", hr);
return hr;
}
}
// RESET Session flags that need to be reset on every connect
m_fSessionLost = FALSE;
m_hrDisconnectResult = DV_OK;
m_fLocalPlayerNotify = FALSE;
m_fLocalPlayerAvailable = FALSE;
m_dwHostOrderID = DVPROTOCOL_HOSTORDER_INVALID;
m_hPlaybackThreadHandle = NULL;
m_hRecordThreadHandle = NULL;
m_thTimerInfo.hPlaybackTimerEvent = NULL;
m_thTimerInfo.lPlaybackCount = 0;
m_thTimerInfo.hRecordTimerEvent = NULL;
m_lpdvfCompressionInfo = NULL;
m_hrConnectResult = DVERR_GENERIC;
m_hrOriginalConnectResult = DVERR_GENERIC;
// Ok not to use interlocked functions as this should be only thread running w/voice
m_iPlayerListReceived = -1;
m_fDisconnecting = FALSE;
ClientStats_Reset();
// Add a reference to incoming objects
if( m_dvSoundDeviceConfig.lpdsPlaybackDevice != NULL )
{
m_dvSoundDeviceConfig.lpdsPlaybackDevice->AddRef();
}
if( m_dvSoundDeviceConfig.lpdsMainBuffer != NULL )
{
m_dvSoundDeviceConfig.lpdsMainBuffer->AddRef();
}
// Add a reference to incoming objects
if( m_dvSoundDeviceConfig.lpdsCaptureDevice != NULL )
{
m_dvSoundDeviceConfig.lpdsCaptureDevice->AddRef();
}
DNEnterCriticalSection( &m_lockPlaybackMode );
m_dwActiveCount = 0;
m_dwEchoState = DVCECHOSTATE_IDLE;
DNLeaveCriticalSection( &m_lockPlaybackMode );
// Need to reset disconnect event manually
ResetEvent( m_hDisconnectAck );
// This was here to disable capture
//
// m_dvSoundDeviceConfig.dwFlags |= DVSOUNDCONFIG_NOFOCUS;
m_dvSoundDeviceConfig.dwMainBufferFlags |= DSBPLAY_LOOPING;
memcpy( &m_dvClientConfig, lpClientConfig, sizeof( DVCLIENTCONFIG ) );
// Check for duplicate objects in the process, re-use existing object if there is one.
// Also does some sanity checks.
hr = CheckForDuplicateObjects();
if( FAILED(hr) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error checking dsound(cap) objects hr=0x%x", hr );
goto CONNECT_ERROR;
}
#ifdef __FORCEFULLDUPLEX
m_dvSoundDeviceConfig.dwFlags |= DVSOUNDCONFIG_HALFDUPLEX;
#endif
if( m_dvClientConfig.dwBufferAggressiveness == DVBUFFERAGGRESSIVENESS_DEFAULT )
{
m_dvClientConfig.dwBufferAggressiveness = s_dwDefaultBufferAggressiveness;
}
if( m_dvClientConfig.dwBufferQuality == DVBUFFERQUALITY_DEFAULT )
{
m_dvClientConfig.dwBufferQuality = s_dwDefaultBufferQuality;
}
if( m_dvClientConfig.dwThreshold == DVTHRESHOLD_DEFAULT )
{
m_dvClientConfig.dwThreshold = s_dwDefaultSensitivity;
}
m_dwMigrateHostOrderID = DVPROTOCOL_HOSTORDER_INVALID;
m_dwLastConnectSent = 0;
m_dwSynchBegin = 0;
SetCurrentState( DVCSTATE_CONNECTING );
// Initialize the name table
m_voiceNameTable.Initialize();
// Initialize bilinks -- if we fail on our connect things won't go south.
m_dwPlayActiveCount = 0;
m_blPlayActivePlayers.Initialize();
m_blPlayAddPlayers.Initialize();
m_blNotifyActivePlayers.Initialize();
m_blNotifyAddPlayers.Initialize();
m_pStatsBlob = &m_stats;
m_fpPlayers.Initialize(sizeof(CVoicePlayer), CVoicePlayer::PoolAllocFunction,
NULL, NULL, CVoicePlayer::PoolDeallocFunction);
hr = SetupInitialBuffers();
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "SetupBuffersInitial Failed 0x%x", hr );
goto CONNECT_ERROR;
}
hr = m_lpSessionTransport->EnableReceiveHook( m_lpObject, DVTRANSPORT_OBJECTTYPE_CLIENT );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "EnableReceiveHook Failed 0x%x", hr );
goto CONNECT_ERROR;
}
m_fConnectAsync = !(dwFlags & DVFLAGS_SYNC);
m_dvidServer = m_lpSessionTransport->GetServerID();
m_dvidLocal = m_lpSessionTransport->GetLocalID();
// Send connect request to the server
guardLock.Unlock();
m_dwLastConnectSent = GetTickCount();
m_dwSynchBegin = m_dwLastConnectSent;
hr = Send_ConnectRequest();
DPFX(DPFPREP, DVF_INFOLEVEL, "DVCE::Connect() - Sending Request to server" );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error on send 0x%x", hr );
goto CONNECT_ERROR;
}
// If the user wants us to wait for the response, wait here.
if( dwFlags & DVFLAGS_SYNC )
{
DPFX(DPFPREP, DVF_INFOLEVEL, "Sync flag, waiting for completion." );
// Wait until we're called with the appropriate message
WaitForSingleObject( m_hConnectAck, INFINITE );
DPFX(DPFPREP, DVF_INFOLEVEL, "Server response received" );
return GetConnectResult();
}
return DVERR_PENDING;
CONNECT_ERROR:
// Release any objects we are holding.. if we have created a sound device
// reference. I.e. we've linked to an inproc sound object
if( lpSoundDeviceConfig->lpdsPlaybackDevice == NULL &&
m_dvSoundDeviceConfig.lpdsPlaybackDevice != NULL )
{
m_dvSoundDeviceConfig.lpdsPlaybackDevice->Release();
m_dvSoundDeviceConfig.lpdsPlaybackDevice = NULL;
}
if( m_dvSoundDeviceConfig.lpdsMainBuffer != NULL )
{
m_dvSoundDeviceConfig.lpdsMainBuffer->Release();
m_dvSoundDeviceConfig.lpdsMainBuffer = NULL;
}
// Release any objects we are holding
// i.e. we've linked to an inproc sound object
if( lpSoundDeviceConfig->lpdsCaptureDevice == NULL &&
m_dvSoundDeviceConfig.lpdsCaptureDevice != NULL )
{
m_dvSoundDeviceConfig.lpdsCaptureDevice->Release();
m_dvSoundDeviceConfig.lpdsCaptureDevice = NULL;
}
SetCurrentState( DVCSTATE_IDLE );
FreeBuffers();
m_voiceNameTable.DeInitialize((m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_PEER),m_lpUserContext,m_lpMessageHandler);
m_fpPlayers.DeInitialize();
return hr;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::Send_SessionLost"
HRESULT CDirectVoiceClientEngine::Send_SessionLost()
{
PDVPROTOCOLMSG_SESSIONLOST pSessionLost;
PDVTRANSPORT_BUFFERDESC pBufferDesc;
LPVOID pvSendContext;
HRESULT hr;
pBufferDesc = GetTransmitBuffer( sizeof( DVPROTOCOLMSG_SESSIONLOST ), &pvSendContext );
if( pBufferDesc == NULL )
{
return DVERR_OUTOFMEMORY;
}
pSessionLost = (PDVPROTOCOLMSG_SESSIONLOST) pBufferDesc->pBufferData;
// Send connection request to the server
pSessionLost->dwType = DVMSGID_SESSIONLOST;
pSessionLost->hresReason = DVERR_SESSIONLOST;
// Fixed so that it gets sent
hr = m_lpSessionTransport->SendToAll( pBufferDesc, pvSendContext, DVTRANSPORT_SEND_GUARANTEED );
if( hr != DVERR_PENDING && hr != DV_OK )
{
DPFX(DPFPREP, 0, "Error sending connect request hr=0x%x", hr );
ReturnTransmitBuffer( pvSendContext );
}
// Pending = OK = expected
else
{
hr = DV_OK;
}
return hr;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::Send_ConnectRequest"
HRESULT CDirectVoiceClientEngine::Send_ConnectRequest()
{
PDVPROTOCOLMSG_CONNECTREQUEST pConnectRequest;
PDVTRANSPORT_BUFFERDESC pBufferDesc;
LPVOID pvSendContext;
HRESULT hr;
pBufferDesc = GetTransmitBuffer( sizeof( DVPROTOCOLMSG_CONNECTREQUEST ), &pvSendContext );
if( pBufferDesc == NULL )
{
return DVERR_OUTOFMEMORY;
}
pConnectRequest = (PDVPROTOCOLMSG_CONNECTREQUEST) pBufferDesc->pBufferData;
// Send connection request to the server
pConnectRequest->dwType = DVMSGID_CONNECTREQUEST;
pConnectRequest->ucVersionMajor = DVPROTOCOL_VERSION_MAJOR;
pConnectRequest->ucVersionMinor = DVPROTOCOL_VERSION_MINOR;
pConnectRequest->dwVersionBuild = DVPROTOCOL_VERSION_BUILD;
hr = m_lpSessionTransport->SendToServer( pBufferDesc, pvSendContext, DVTRANSPORT_SEND_GUARANTEED );
if( hr != DVERR_PENDING && hr != DV_OK )
{
DPFX(DPFPREP, 0, "Error sending connect request hr=0x%x", hr );
}
// Pending = OK = expected
else
{
hr = DV_OK;
}
return hr;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::Send_DisconnectRequest"
HRESULT CDirectVoiceClientEngine::Send_DisconnectRequest()
{
PDVPROTOCOLMSG_GENERIC pDisconnectRequest;
PDVTRANSPORT_BUFFERDESC pBufferDesc;
LPVOID pvSendContext;
HRESULT hr;
pBufferDesc = GetTransmitBuffer( sizeof( DVPROTOCOLMSG_GENERIC ), &pvSendContext );
if( pBufferDesc == NULL )
{
return DVERR_OUTOFMEMORY;
}
pDisconnectRequest = (PDVPROTOCOLMSG_GENERIC) pBufferDesc->pBufferData;
// Send connection request to the server
pDisconnectRequest->dwType = DVMSGID_DISCONNECT;
hr = m_lpSessionTransport->SendToServer( pBufferDesc, pvSendContext, DVTRANSPORT_SEND_GUARANTEED );
if( hr != DVERR_PENDING && hr != DV_OK )
{
DPFX(DPFPREP, 0, "Error sending connect request hr=0x%x", hr );
}
// Pending = OK = expected
else
{
hr = DV_OK;
}
return hr;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::Send_SettingsConfirm"
HRESULT CDirectVoiceClientEngine::Send_SettingsConfirm()
{
PDVPROTOCOLMSG_SETTINGSCONFIRM pSettingsConfirm;
PDVTRANSPORT_BUFFERDESC pBufferDesc;
LPVOID pvSendContext;
HRESULT hr;
pBufferDesc = GetTransmitBuffer( sizeof( DVPROTOCOLMSG_SETTINGSCONFIRM ), &pvSendContext );
if( pBufferDesc == NULL )
{
return DVERR_OUTOFMEMORY;
}
pSettingsConfirm = (PDVPROTOCOLMSG_SETTINGSCONFIRM) pBufferDesc->pBufferData;
// Send connection request to the server
pSettingsConfirm->dwType = DVMSGID_SETTINGSCONFIRM;
pSettingsConfirm->dwHostOrderID = m_dwHostOrderID;
pSettingsConfirm->dwFlags = 0;
if( m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_HALFDUPLEX )
{
pSettingsConfirm->dwFlags |= DVPLAYERCAPS_HALFDUPLEX;
}
hr = m_lpSessionTransport->SendToServer( pBufferDesc, pvSendContext, DVTRANSPORT_SEND_GUARANTEED );
if( hr != DVERR_PENDING && hr != DV_OK )
{
DPFX(DPFPREP, 0, "Error sending connect request hr=0x%x", hr );
}
// Pending = OK = expected
else
{
hr = DV_OK;
}
return hr;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::Disconnect"
// Disconnect
//
// Implements the IDirectXVoiceClient::Disconnect function
//
// Locks Required:
// - Global Lock
//
// Called By:
// DVC_Disconnect
//
HRESULT CDirectVoiceClientEngine::Disconnect( DWORD dwFlags )
{
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Enter" );
DPFX(DPFPREP, DVF_APIPARAM, "dwFlags = 0x%x", dwFlags );
HRESULT hr;
if( dwFlags & ~(DVFLAGS_SYNC) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid flags specified" );
return DVERR_INVALIDFLAGS;
}
CDVCSLock guardLock(&m_csClassLock);
guardLock.Lock();
if( m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Not initialized" );
return DVERR_NOTINITIALIZED;
}
if( m_dwCurrentState == DVCSTATE_CONNECTING )
{
m_fDisconnectAsync = !(dwFlags & DVFLAGS_SYNC);
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "DVCE::Disconnect() Abort connection" );
// Handle Connect
SetConnectResult( DVERR_CONNECTABORTED );
SendConnectResult();
SetEvent( m_hConnectAck );
DoSignalDisconnect( DVERR_CONNECTABORTED );
guardLock.Unlock();
if( dwFlags & DVFLAGS_SYNC )
{
goto DISCONNECT_WAIT;
}
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Returning DVERR_CONNECTABORTING" );
return DVERR_CONNECTABORTING;
}
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "State Good.." );
if( m_dwCurrentState == DVCSTATE_DISCONNECTING )
{
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Already disconnecting. Waiting on current event" );
guardLock.Unlock();
if( dwFlags & DVFLAGS_SYNC )
{
goto DISCONNECT_WAIT;
}
return DVERR_ALREADYPENDING;
}
else if( m_dwCurrentState != DVCSTATE_CONNECTED )
{
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Not Connected" );
DPFX(DPFPREP, DVF_APIPARAM, "Returning DVERR_NOTCONNECTED" );
return DVERR_NOTCONNECTED;
}
else
{
m_fDisconnectAsync = !(dwFlags & DVFLAGS_SYNC);
m_dwSynchBegin = GetTickCount();
// Set current state to disconnecting before we release the lock
SetCurrentState( DVCSTATE_DISCONNECTING );
guardLock.Unlock();
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Disconnect request about to be sent" );
hr = Send_DisconnectRequest();
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Disconnect request transmitted hr=0x%x", hr );
guardLock.Lock();
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "DVCE::Disconnect - Error on send 0x%x", hr );
// Inform notify thread to disconnect, since send failed there won't be a confirm
SetEvent( m_hNotifyDisconnect );
hr = DV_OK;
}
else
{
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Disconnect sent" );
}
}
guardLock.Unlock();
if( dwFlags & DVFLAGS_SYNC )
{
goto DISCONNECT_WAIT;
}
DPFX(DPFPREP, DVF_INFOLEVEL, "Returning DVERR_PENDING" );
return DVERR_PENDING;
// You should have dropped the Write Loc+k by now
DISCONNECT_WAIT:
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Sync flag, waiting for completion." );
WaitForSingleObject( m_hDisconnectAck, INFINITE );
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Server response received" );
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Disconnect Result = 0x%x", m_hrDisconnectResult );
return m_hrDisconnectResult;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::GetSessionDesc"
// GetSessionDesc
//
// Retrieves the current session description.
//
// Called By:
// DVC_GetSessionDesc
//
// Locks Required:
// - Global Read Lock
//
HRESULT CDirectVoiceClientEngine::GetSessionDesc( PDVSESSIONDESC lpSessionDesc )
{
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Enter" );
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
DPFX(DPFPREP, DVF_APIPARAM, "lpSessionDescBuffer = 0x%p", lpSessionDesc );
if( lpSessionDesc == NULL || !DNVALID_WRITEPTR(lpSessionDesc,sizeof( DVSESSIONDESC )) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Session desc pointer bad" );
return DVERR_INVALIDPOINTER;
}
if( lpSessionDesc->dwSize != sizeof( DVSESSIONDESC ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid size on session desc" );
return DVERR_INVALIDPARAM;
}
CDVCSLock guardLock(&m_csClassLock);
guardLock.Lock();
if( m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Object not initalized" );
return DVERR_NOTINITIALIZED;
}
if( m_dwCurrentState != DVCSTATE_CONNECTED )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Not connected" );
return DVERR_NOTCONNECTED;
}
memcpy( lpSessionDesc, &m_dvSessionDesc, sizeof( DVSESSIONDESC ) );
DV_DUMP_SD( (LPDVSESSIONDESC) lpSessionDesc );
DPFX(DPFPREP, DVF_APIPARAM, "Returning DV_OK" );
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::GetSoundDeviceConfig"
// GetSoundDeviceConfig
//
// Retrieves the current client configuration.
//
// Called By:
// DVC_GetSoundDeviceConfig
//
// Locks Required:
// - Global Read Lock
HRESULT CDirectVoiceClientEngine::GetSoundDeviceConfig( PDVSOUNDDEVICECONFIG pSoundDeviceConfig, PDWORD pdwBufferSize )
{
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Enter" );
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
DPFX(DPFPREP, DVF_APIPARAM, "pSoundDeviceConfig = 0x%p", pSoundDeviceConfig );
DWORD dwRequiredSize = sizeof(DVSOUNDDEVICECONFIG);
if( pdwBufferSize == NULL ||
!DNVALID_WRITEPTR(pdwBufferSize,sizeof(DWORD)) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid pointer" );
return DVERR_INVALIDPOINTER;
}
if( pSoundDeviceConfig != NULL &&
!DNVALID_WRITEPTR(pSoundDeviceConfig,*pdwBufferSize ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid pointer" );
return DVERR_INVALIDPOINTER;
}
if( pSoundDeviceConfig != NULL && pSoundDeviceConfig->dwSize != sizeof( DVSOUNDDEVICECONFIG ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid Size on clientconfig" );
return DVERR_INVALIDPARAM;
}
CDVCSLock guardLock(&m_csClassLock);
guardLock.Lock();
if( m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Object not initialized" );
return DVERR_NOTINITIALIZED;
}
if( m_dwCurrentState != DVCSTATE_CONNECTED )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Not Connected" );
return DVERR_NOTCONNECTED;
}
if( *pdwBufferSize < dwRequiredSize || pSoundDeviceConfig == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Buffer too small!" );
*pdwBufferSize = dwRequiredSize;
return DVERR_BUFFERTOOSMALL;
}
memcpy( pSoundDeviceConfig, &m_dvSoundDeviceConfig, sizeof( DVSOUNDDEVICECONFIG ) );
DV_DUMP_SDC( pSoundDeviceConfig );
// # of bytes written
*pdwBufferSize = dwRequiredSize;
DPFX(DPFPREP, DVF_ENTRYLEVEL, "End" );
DPFX(DPFPREP, DVF_APIPARAM, "Returning DV_OK" );
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::GetClientConfig"
// GetClientConfig
//
// Retrieves the current client configuration.
//
// Called By:
// DVC_GetClientConfig
//
// Locks Required:
// - Global Read Lock
HRESULT CDirectVoiceClientEngine::GetClientConfig( LPDVCLIENTCONFIG lpClientConfig )
{
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Enter" );
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
DPFX(DPFPREP, DVF_APIPARAM, "lpClientConfig = 0x%p", lpClientConfig );
if( lpClientConfig == NULL ||
!DNVALID_WRITEPTR(lpClientConfig,sizeof(DVCLIENTCONFIG) ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid pointer" );
return E_POINTER;
}
if( lpClientConfig->dwSize != sizeof( DVCLIENTCONFIG ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid Size on clientconfig" );
return DVERR_INVALIDPARAM;
}
CDVCSLock guardLock(&m_csClassLock);
guardLock.Lock();
if( m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Object not initialized" );
return DVERR_NOTINITIALIZED;
}
if( m_dwCurrentState != DVCSTATE_CONNECTED )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Not Connected" );
return DVERR_NOTCONNECTED;
}
memcpy( lpClientConfig, &m_dvClientConfig, sizeof( DVCLIENTCONFIG ) );
if( lpClientConfig->dwFlags & DVCLIENTCONFIG_AUTOVOICEACTIVATED )
{
lpClientConfig->dwThreshold = DVTHRESHOLD_UNUSED;
}
DV_DUMP_CC( lpClientConfig );
DPFX(DPFPREP, DVF_ENTRYLEVEL, "End" );
DPFX(DPFPREP, DVF_APIPARAM, "Returning DV_OK" );
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::SetClientConfig"
// SetClientConfig
//
// Sets the current client configuration.
//
// Called By:
// DVC_SetClientConfig
//
// Locks Required:
// - Global Write Lock
//
HRESULT CDirectVoiceClientEngine::SetClientConfig( LPDVCLIENTCONFIG lpClientConfig )
{
HRESULT hr;
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Enter" );
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
DPFX(DPFPREP, DVF_APIPARAM, "lpClientConfig=0x%p", lpClientConfig );
if( lpClientConfig == NULL ||
!DNVALID_READPTR(lpClientConfig,sizeof(DVCLIENTCONFIG)))
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid pointer" );
return E_POINTER;
}
DV_DUMP_CC( lpClientConfig );
if( lpClientConfig->dwSize != sizeof( DVCLIENTCONFIG ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "DVCE::SetClientConfig() Error parameters" );
return DVERR_INVALIDPARAM;
}
hr = DV_ValidClientConfig( lpClientConfig );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error validating Clientconfig hr=0x%x", hr );
return hr;
}
CDVCSLock guardLock(&m_csClassLock);
guardLock.Lock();
if( m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Not initialized" );
return DVERR_NOTINITIALIZED;
}
if( m_dwCurrentState != DVCSTATE_CONNECTED )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Not connected" );
return DVERR_NOTCONNECTED;
}
BOOL bNotifyChange = FALSE,
bPlaybackChange = FALSE,
bRecordChange = FALSE,
bSensitivityChange = FALSE;
// If we're not half duplex, take care of the volume settings
if( !(m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_HALFDUPLEX ) &&
!(m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_NORECVOLAVAILABLE) )
{
if( lpClientConfig->lRecordVolume != DVRECORDVOLUME_LAST)
{
m_dvClientConfig.lRecordVolume = lpClientConfig->lRecordVolume;
m_audioRecordBuffer->SetVolume( m_dvClientConfig.lRecordVolume );
}
}
if( m_dvClientConfig.lPlaybackVolume != lpClientConfig->lPlaybackVolume )
{
m_dvClientConfig.lPlaybackVolume = lpClientConfig->lPlaybackVolume;
SetPlaybackVolume( m_dvClientConfig.lPlaybackVolume );
}
if( m_dvClientConfig.dwNotifyPeriod != lpClientConfig->dwNotifyPeriod )
{
m_dvClientConfig.dwNotifyPeriod = lpClientConfig->dwNotifyPeriod;
SetEvent( m_hNotifyChange );
}
if( !(lpClientConfig->dwFlags & DVCLIENTCONFIG_MANUALVOICEACTIVATED ) )
{
m_dvClientConfig.dwThreshold = DVTHRESHOLD_UNUSED;
}
else if( m_dvClientConfig.dwThreshold != lpClientConfig->dwThreshold )
{
if( lpClientConfig->dwThreshold == DVTHRESHOLD_DEFAULT )
{
m_dvClientConfig.dwThreshold = s_dwDefaultSensitivity;
}
else
{
m_dvClientConfig.dwThreshold = lpClientConfig->dwThreshold;
}
}
if( (lpClientConfig->dwFlags & DVCLIENTCONFIG_ECHOSUPPRESSION) !=
(m_dvClientConfig.dwFlags & DVCLIENTCONFIG_ECHOSUPPRESSION) )
{
DNEnterCriticalSection( &m_lockPlaybackMode );
m_dwEchoState = DVCECHOSTATE_IDLE;
DNLeaveCriticalSection( &m_lockPlaybackMode );
}
if( m_dvClientConfig.dwBufferAggressiveness == DVBUFFERAGGRESSIVENESS_DEFAULT )
{
m_dvClientConfig.dwBufferAggressiveness = s_dwDefaultBufferAggressiveness;
}
if( m_dvClientConfig.dwBufferQuality == DVBUFFERQUALITY_DEFAULT )
{
m_dvClientConfig.dwBufferQuality = s_dwDefaultBufferQuality;
}
// If we haven't specified NOFOCUS and we're not half duplex
if( !(m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_HALFDUPLEX) &&
!(m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_NOFOCUS) )
{
// If the settings have changed
if( (m_dvClientConfig.dwFlags & DVCLIENTCONFIG_RECORDMUTE) !=
(lpClientConfig->dwFlags & DVCLIENTCONFIG_RECORDMUTE) )
{
if( lpClientConfig->dwFlags & DVCLIENTCONFIG_RECORDMUTE )
{
DPFX(DPFPREP, DVF_INFOLEVEL, "Record Muted: Yielding focus" );
hr = m_audioRecordBuffer->YieldFocus();
}
else
{
DPFX(DPFPREP, DVF_INFOLEVEL, "Record Un-Muted: Attempting to reclaim focus" );
hr = m_audioRecordBuffer->ClaimFocus();
}
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Focus set failed hr=0x%x", hr );
}
}
}
m_dvClientConfig.dwFlags = lpClientConfig->dwFlags;
m_dvClientConfig.dwNotifyPeriod = lpClientConfig->dwNotifyPeriod;
guardLock.Unlock();
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Returning DV_OK" );
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::GetCaps"
//
// GetCaps
//
// This function retrieves the caps structure for this DirectPlayVoiceClient
// object.
//
// Called By:
// - DVC_GetCaps
//
// Locks Required:
// - Global Read Lock
//
HRESULT CDirectVoiceClientEngine::GetCaps( LPDVCAPS lpCaps )
{
DPFX(DPFPREP, DVF_ENTRYLEVEL, "DVCE::GetCaps() Begin" );
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
DPFX(DPFPREP, DVF_APIPARAM, "Params: lpCaps: 0x%p", lpCaps );
CDVCSLock guardLock(&m_csClassLock);
if( lpCaps == NULL ||
!DNVALID_WRITEPTR(lpCaps,sizeof(DVCAPS)))
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid pointer" );
return DVERR_INVALIDPOINTER;
}
if( lpCaps->dwSize != sizeof( DVCAPS ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error parameters" );
return DVERR_INVALIDPARAM;
}
guardLock.Lock();
memcpy( lpCaps, &m_dvCaps, sizeof( DVCAPS ) );
guardLock.Unlock();
DV_DUMP_CAPS( lpCaps );
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Done" );
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::GetCompressionTypes"
//
// GetCompressionTypes
//
// Retrieves configured compression types for this object.
//
// Called By:
// - DVC_GetCompressionTypes
//
// Locks Required:
// - Global Read Lock
//
HRESULT CDirectVoiceClientEngine::GetCompressionTypes( LPVOID lpBuffer, LPDWORD lpdwSize, LPDWORD lpdwNumElements, DWORD dwFlags )
{
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Begin" );
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
DPFX(DPFPREP, DVF_APIPARAM, "Params: lpBuffer = 0x%p lpdwSize = 0x%p lpdwNumElements = 0x%p, dwFlags = 0x%x",
lpBuffer, lpdwSize, lpdwNumElements, dwFlags );
CDVCSLock guardLock(&m_csClassLock);
guardLock.Lock();
HRESULT hres = DVCDB_CopyCompressionArrayToBuffer( lpBuffer, lpdwSize, lpdwNumElements, dwFlags );
guardLock.Unlock();
if( hres == DV_OK )
{
DV_DUMP_CI( (LPDVCOMPRESSIONINFO) lpBuffer, *lpdwNumElements );
}
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Done" );
return hres;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::CheckForAndRemoveTarget"
//
// CheckForAndRemoveTarget
//
// Checks the current target list for the specified ID and removes it from
// the target list if it is in the target list.
//
HRESULT CDirectVoiceClientEngine::CheckForAndRemoveTarget( DVID dvidID )
{
HRESULT hr = DV_OK;
DNEnterCriticalSection( &m_csTargetLock );
// Search the list of targets
for( DWORD dwIndex = 0; dwIndex < m_dwNumTargets; dwIndex++ )
{
if( m_pdvidTargets[dwIndex] == dvidID )
{
if( m_dwNumTargets == 1 )
{
hr = InternalSetTransmitTarget( NULL, 0 );
}
// We'll re-use the current target array
else
{
// Collapse the list by either ommiting the last element (if the
// one we want to remove is last, or by moving last element into
// the place in the list we're removing.
if( dwIndex+1 != m_dwNumTargets )
{
m_pdvidTargets[dwIndex] = m_pdvidTargets[m_dwNumTargets-1];
}
hr = InternalSetTransmitTarget( m_pdvidTargets, m_dwNumTargets-1 );
}
break;
}
}
DNLeaveCriticalSection( &m_csTargetLock );
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::InternalSetTransmitTarget"
//
// InternalSetTransmitTarget
//
// Does the work of setting the target. (Assumes values have been validated).
//
// This function is safe to pass a pointer to the current target array. It works
// on a temporary.
//
HRESULT CDirectVoiceClientEngine::InternalSetTransmitTarget( PDVID pdvidTargets, DWORD dwNumTargets )
{
DWORD dwRequiredSize;
PBYTE pbDataBuffer;
PDVMSG_SETTARGETS pdvSetTarget;
DNEnterCriticalSection( &m_csTargetLock );
// No targets? set list to NULL
if( dwNumTargets == 0 )
{
// Close memory leak
//
// Hawk Bug #
//
if( m_pdvidTargets != NULL )
{
delete [] m_pdvidTargets;
}
m_pdvidTargets = NULL;
}
// Otherwise allocate new list and copy
else
{
PDVID pTmpTargetList;
pTmpTargetList = new DVID[dwNumTargets];
if( pTmpTargetList == NULL )
{
delete [] m_pdvidTargets;
m_dwNumTargets = 0;
DPFX(DPFPREP, DVF_ERRORLEVEL, "Memory alloc failure" );
DNLeaveCriticalSection( &m_csTargetLock );
return DVERR_OUTOFMEMORY;
}
memcpy( pTmpTargetList, pdvidTargets, dwNumTargets*sizeof(DVID) );
// Kill off old target list
if( m_pdvidTargets != NULL )
delete [] m_pdvidTargets;
m_pdvidTargets = pTmpTargetList;
}
m_dwNumTargets = dwNumTargets;
m_dwTargetVersion++;
dwRequiredSize = m_dwNumTargets * sizeof( DVID );
dwRequiredSize += sizeof( DVMSG_SETTARGETS );
pbDataBuffer = new BYTE[dwRequiredSize];
if( pbDataBuffer == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error allocating memory!" );
DNLeaveCriticalSection( &m_csTargetLock );
return TRUE;
}
pdvSetTarget = (PDVMSG_SETTARGETS) pbDataBuffer;
pdvSetTarget->pdvidTargets = (PDVID) (pbDataBuffer+sizeof(DVMSG_SETTARGETS));
pdvSetTarget->dwNumTargets = m_dwNumTargets;
pdvSetTarget->dwSize = sizeof( DVMSG_SETTARGETS );
memcpy( pdvSetTarget->pdvidTargets, m_pdvidTargets, sizeof(DVID)*m_dwNumTargets );
NotifyQueue_Add( DVMSGID_SETTARGETS, pdvSetTarget, dwRequiredSize );
delete [] pbDataBuffer;
DNLeaveCriticalSection( &m_csTargetLock );
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::SetTransmitTarget"
//
// SetTransmitTarget
//
// Sets the current transmit target.
//
// Called by:
// - DVC_SetTransmitTarget
//
// Locks Required:
// - Global Write Lock
//
HRESULT CDirectVoiceClientEngine::SetTransmitTarget( PDVID pdvidTargets, DWORD dwNumTargets, DWORD dwFlags )
{
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Begin" );
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
DPFX(DPFPREP, DVF_APIPARAM, "Params: pdvidTargets = 0x%p dwNumTargets = %d dwFlags = 0x%x", pdvidTargets, dwNumTargets, dwFlags );
if( dwFlags != 0 )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid flags" );
return DVERR_INVALIDFLAGS;
}
HRESULT hr;
// Check that the target list is valid
hr = DV_ValidTargetList( pdvidTargets, dwNumTargets );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Target list is not valid" );
return hr;
}
DWORD dwIndex;
if( m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Object not initialized" );
return DVERR_NOTINITIALIZED;
}
if( m_dwCurrentState != DVCSTATE_CONNECTED &&
m_dwCurrentState != DVCSTATE_CONNECTING )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Not connected" );
return DVERR_NOTCONNECTED;
}
else
{
if( m_dvSessionDesc.dwFlags & DVSESSION_SERVERCONTROLTARGET )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Denied. Server controlled target" );
return DVERR_NOTALLOWED;
}
if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_PEER )
{
// Loop through target list, confirm they are valid entries
for( dwIndex = 0; dwIndex < dwNumTargets; dwIndex++ )
{
if( !m_voiceNameTable.IsEntry(pdvidTargets[dwIndex]) )
{
if( !m_lpSessionTransport->ConfirmValidGroup( pdvidTargets[dwIndex] ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid target" );
return DVERR_INVALIDTARGET;
}
}
}
}
}
hr = InternalSetTransmitTarget( pdvidTargets, dwNumTargets );
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Done" );
return hr;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::GetTransmitTarget"
//
// GetTransmitTarget
//
// Retrieves the current transmission target.
//
// Called By:
// - DVC_GetTransmitTarget
//
// Locks Required:
// - Read Lock Required
//
HRESULT CDirectVoiceClientEngine::GetTransmitTarget( LPDVID lpdvidTargets, PDWORD pdwNumElements, DWORD dwFlags )
{
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Begin" );
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
DPFX(DPFPREP, DVF_APIPARAM, "Params: lpdvidTargets = 0x%p pdwNumElements = 0x%x dwFlags = 0x%x", lpdvidTargets, pdwNumElements, dwFlags );
if( pdwNumElements == NULL ||
!DNVALID_WRITEPTR( pdwNumElements, sizeof( DWORD ) ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid pointer to num of elements" );
return DVERR_INVALIDPOINTER;
}
if( pdwNumElements != NULL &&
*pdwNumElements > 0 &&
!DNVALID_WRITEPTR( lpdvidTargets, (*pdwNumElements) * sizeof( DVID) ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid target list buffer specified" );
return DVERR_INVALIDPOINTER;
}
if( pdwNumElements == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Must specify a ptr for # of elements" );
return DVERR_INVALIDPARAM;
}
if( dwFlags != 0 )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid flags" );
return DVERR_INVALIDFLAGS;
}
if( m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Object not initialized" );
return DVERR_NOTINITIALIZED;
}
else if( m_dwCurrentState != DVCSTATE_CONNECTED )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Not Connected" );
DPFX(DPFPREP, DVF_APIPARAM, "Returning DVERR_NOTCONNECTED" );
return DVERR_NOTCONNECTED;
}
HRESULT hr = DV_OK;
DNEnterCriticalSection( &m_csTargetLock );
if( *pdwNumElements < m_dwNumTargets )
{
hr = DVERR_BUFFERTOOSMALL;
}
else
{
memcpy( lpdvidTargets, m_pdvidTargets,m_dwNumTargets*sizeof(DVID) );
}
*pdwNumElements = m_dwNumTargets;
DNLeaveCriticalSection( &m_csTargetLock );
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Success" );
return hr;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::InitSoundTargetList"
//
// InitSoundTargetList
//
// Initializes the sound target list.
//
// Called By:
// - InitializeSoundSystem
//
// Locks Required:
// - Buffer Lock
//
HRESULT CDirectVoiceClientEngine::InitSoundTargetList()
{
DNEnterCriticalSection( &m_csBufferLock );
m_lpstBufferList = NULL;
DNLeaveCriticalSection( &m_csBufferLock );
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::FreeSoundTargetList"
//
// FreeSoundTargetList
//
// Releases the sound target list.
//
// Also cleans up buffers not released by the user. This must be called before the playback system
// is shutdown.
//
// Called By:
// - DeInitializeSoundSystem
//
// Locks Required:
// - Buffer Lock
//
HRESULT CDirectVoiceClientEngine::FreeSoundTargetList()
{
CSoundTarget *lpctFinder, *lpctLast;
LONG lRefCount;
DVID dvid;
DNEnterCriticalSection( &m_csBufferLock );
lpctLast = NULL;
lpctFinder = m_lpstBufferList;
DPFX(DPFPREP, DVF_SOUNDTARGET_DEBUG_LEVEL, "SOUNDTARGET: DESTROYING OPEN TARGET OBJECTS" );
CHECKLISTINTEGRITY();
// If we enter this loop we're in a questionable state.
//
// The user hasn't called Delete3DSoundBuffer on one or more buffers
//
// We're going to cleanup, if they attempt to access the pointers after this point
// the app will access violate.
//
while( lpctFinder != NULL )
{
lpctLast = lpctFinder;
lpctFinder = lpctFinder->m_lpstNext;
lRefCount = lpctLast->GetRefCount();
DNASSERT( lRefCount == 2 );
CHECKLISTINTEGRITY();
DPFX(DPFPREP, DVF_ERRORLEVEL, "=========================================================================" );
DPFX(DPFPREP, DVF_ERRORLEVEL, "3D SoundBuffer for ID 0x%x was not released, cleaning it up", lpctLast->GetTarget() );
DPFX(DPFPREP, DVF_ERRORLEVEL, "This is an ERROR. You must Delete3DSoundBuffer before closing the interface." );
DPFX(DPFPREP, DVF_ERRORLEVEL, "DirectPlayVoice has freed the resources, so if you access them you will crash." );
DPFX(DPFPREP, DVF_ERRORLEVEL, "=========================================================================" );
lpctLast->Release();
DeleteSoundTarget( lpctLast->GetTarget() );
CHECKLISTINTEGRITY();
}
DPFX(DPFPREP, DVF_SOUNDTARGET_DEBUG_LEVEL, "SOUNDTARGET: DESTROYING GENERAL BUFFER" );
if( m_lpstGeneralBuffer != NULL )
{
// Release the core's reference to the buffer
lRefCount = m_lpstGeneralBuffer->Release();
// User must not have freed the buffer
if( lRefCount > 0 )
{
DNASSERT( lRefCount == 1 );
DPFX(DPFPREP, DVF_ERRORLEVEL, "=========================================================================" );
DPFX(DPFPREP, DVF_ERRORLEVEL, "Main 3D SoundBuffer was not released, cleaning it up" );
DPFX(DPFPREP, DVF_ERRORLEVEL, "This is an ERROR. You must Delete3DSoundBuffer before closing the interface." );
DPFX(DPFPREP, DVF_ERRORLEVEL, "DirectPlayVoice has freed the resources, so if you access them you will crash." );
DPFX(DPFPREP, DVF_ERRORLEVEL, "=========================================================================" );
m_lpstGeneralBuffer->Release();
m_lpstGeneralBuffer = NULL;
}
else
{
m_lpstGeneralBuffer = NULL;
}
// Releasing the buffer above released this object as well
m_audioPlaybackBuffer = NULL;
}
DNLeaveCriticalSection( &m_csBufferLock );
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::AddSoundTarget"
//
// AddSoundTarget
//
// Adds a new target to the sound target list
//
// Called By:
// - CreateUserBuffer
//
// Locks Required:
// - Buffer Lock
//
HRESULT CDirectVoiceClientEngine::AddSoundTarget( CSoundTarget *lpcsTarget )
{
DNEnterCriticalSection( &m_csBufferLock );
DPFX(DPFPREP, DVF_SOUNDTARGET_DEBUG_LEVEL, "SOUNDTARGET: [0x%x] ADDSOUNDTARGET", lpcsTarget->GetTarget() );
CHECKLISTINTEGRITY();
lpcsTarget->m_lpstNext = m_lpstBufferList;
m_lpstBufferList = lpcsTarget;
CHECKLISTINTEGRITY();
DNLeaveCriticalSection( &m_csBufferLock );
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::CheckListIntegrity"
void CDirectVoiceClientEngine::CheckListIntegrity()
{
CSoundTarget *lpctFinder;
DNEnterCriticalSection( &m_csBufferLock );
lpctFinder = m_lpstBufferList;
while( lpctFinder != NULL )
{
if( lpctFinder != NULL )
{
DNASSERT( lpctFinder->m_dwSignature == VSIG_SOUNDTARGET );
DNASSERT( lpctFinder->GetRefCount() > 0 );
DNASSERT( lpctFinder->GetRefCount() <= 3 );
DNASSERT( lpctFinder->GetBuffer() != NULL );
}
lpctFinder = lpctFinder->m_lpstNext;
}
DNLeaveCriticalSection( &m_csBufferLock );
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::DeleteSoundTarget"
//
// DeleteSoundTarget
//
// Removes the specified ID's entry from the sound target list
//
// Called By:
// - DeleteSoundTarget
//
// Locks Required:
// - Buffer lock
//
HRESULT CDirectVoiceClientEngine::DeleteSoundTarget( DVID dvidID )
{
CSoundTarget *lpctFinder, *lpctLast, *lpctNext;
LONG lRefCount;
DNEnterCriticalSection( &m_csBufferLock );
DPFX(DPFPREP, DVF_SOUNDTARGET_DEBUG_LEVEL, "SOUNDTARGET: [0x%x] DELETESOUNDTARGET ", dvidID );
lpctLast = NULL;
lpctFinder = m_lpstBufferList;
while( lpctFinder != NULL )
{
CHECKLISTINTEGRITY();
if( lpctFinder->GetTarget() == dvidID )
{
// Store next pointer
lpctNext = lpctFinder->m_lpstNext;
// Release the reference the core has
// If this is the last reference, it destroys the object
//
// If user is holding reference this won't destroy
// the object, the cleanup will.
//
lRefCount = lpctFinder->Release();
// Only remove from list if reference count is 0.
//
// Otherwise you end up with buffer replaying old
// audio
//
DPFX(DPFPREP, DVF_SOUNDTARGET_DEBUG_LEVEL, "SOUNDTARGET: [0x%x] REMOVING FROM LIST ", dvidID );
/* DNASSERT( lRefCount == 0 );
if( lRefCount == 0 )
{ */
if( lpctLast == NULL )
{
m_lpstBufferList = lpctNext;
}
else
{
lpctLast->m_lpstNext = lpctNext;
}
// }
CHECKLISTINTEGRITY();
DNLeaveCriticalSection( &m_csBufferLock );
return DV_OK;
}
lpctLast = lpctFinder;
lpctFinder = lpctFinder->m_lpstNext;
}
DNLeaveCriticalSection( &m_csBufferLock );
return DVERR_INVALIDPLAYER;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::FindSoundTarget"
//
// FindSoundTarget
//
// Look for sound target buffer for the specified user.
//
// If it exists, return it in lpcsTarget
//
// Called By:
// - CreateUserBuffer
//
// Locks Required:
// - Buffer Lock
//
HRESULT CDirectVoiceClientEngine::FindSoundTarget( DVID dvidID, CSoundTarget **lpcsTarget )
{
DNEnterCriticalSection( &m_csBufferLock );
CHECKLISTINTEGRITY();
*lpcsTarget = NULL;
CSoundTarget *lpctFinder;
lpctFinder = m_lpstBufferList;
while( lpctFinder != NULL )
{
if( lpctFinder->GetTarget() == dvidID )
{
*lpcsTarget = lpctFinder;
lpctFinder->AddRef();
DNLeaveCriticalSection( &m_csBufferLock );
return DV_OK;
}
lpctFinder = lpctFinder->m_lpstNext;
}
CHECKLISTINTEGRITY();
DNLeaveCriticalSection( &m_csBufferLock );
return DVERR_INVALIDPLAYER;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::SetupPlaybackBufferDesc"
void CDirectVoiceClientEngine::SetupPlaybackBufferDesc( LPDSBUFFERDESC lpdsBufferDesc, LPDSBUFFERDESC lpdsBufferSource )
{
DV_SetupBufferDesc( lpdsBufferDesc, lpdsBufferSource, s_lpwfxPlaybackFormat, m_dwUnCompressedFrameSize*m_dwNumPerBuffer );
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::Create3DSoundBuffer"
//
// Create3DSoundBuffer
//
// Creates a mixing buffer (a sound target) for the specified user ID.
//
// Called By:
// - DVC_CreateUserBuffer
//
// Locks Required:
// - Global Read Lock
//
HRESULT CDirectVoiceClientEngine::Create3DSoundBuffer( DVID dvidID, LPDIRECTSOUNDBUFFER lpdsBuffer, DWORD dwPriority, DWORD dwFlags, LPDIRECTSOUND3DBUFFER *lpBuffer )
{
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Begin" );
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
DPFX(DPFPREP, DVF_APIPARAM, "Params: dvidID = 0x%x lpdsBuffer = 0x%p dwPriority = 0x%x dwFlags = 0x%x lpBuffer = 0x%p", dvidID, lpdsBuffer, dwPriority, dwFlags, lpBuffer );
HRESULT hr;
if( lpBuffer == NULL ||
!DNVALID_WRITEPTR( lpBuffer, sizeof( LPDIRECTSOUND3DBUFFER ) ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid pointer" );
return DVERR_INVALIDPOINTER;
}
if( dvidID == DVID_REMAINING )
{
if( lpdsBuffer != NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Cannot specify a buffer for the DVID_REMAINING buffer" );
DPFX(DPFPREP, DVF_ERRORLEVEL, "You can set these values from the SoundDeviceConfig structure" );
return DVERR_INVALIDPARAM;
}
if( dwFlags != 0 || dwPriority != 0 )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Cannot specify non-zero flags for voice management for DVID_REMAINING buffer" );
DPFX(DPFPREP, DVF_ERRORLEVEL, "You can set these values from the SoundDeviceConfig structure" );
return DVERR_INVALIDFLAGS;
}
}
else
{
hr = DV_ValidBufferSettings( lpdsBuffer, dwPriority, dwFlags, s_lpwfxPlaybackFormat );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid settings for buffer description hr=0x%x", hr );
return hr;
}
dwFlags |= DSBPLAY_LOOPING;
}
CDVCSLock guardLock(&m_csClassLock);
guardLock.Lock();
if( m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Not initialized" );
return DVERR_NOTINITIALIZED;
}
else if( m_dwCurrentState != DVCSTATE_CONNECTED )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Not connected" );
return DVERR_NOTCONNECTED;
}
if( this->m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_MIXING &&
dvidID != DVID_REMAINING )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Only DVID_REMAINING can be spatialized in mixing sessions" );
return DVERR_NOTALLOWED;
}
if( dvidID == m_dvidLocal )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Cannot create buffer for local player!" );
return DVERR_INVALIDPLAYER;
}
if( dvidID != DVID_ALLPLAYERS &&
dvidID != DVID_REMAINING &&
!m_voiceNameTable.IsEntry(dvidID) )
{
guardLock.Unlock();
if( !m_lpSessionTransport->ConfirmValidGroup( dvidID ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid player/group ID" );
return DVERR_INVALIDPLAYER;
}
guardLock.Lock();
}
DWORD dwMode;
// Handle request for 3d buffer on the main buffer
if( dvidID == DVID_REMAINING )
{
LPDIRECTSOUND3DBUFFER lpds3dTmp;
lpds3dTmp = m_lpstGeneralBuffer->Get3DBuffer();
if( lpds3dTmp == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "No 3d support" );
return DVERR_NO3DSOUND;
}
hr = lpds3dTmp->GetMode( &dwMode );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to get 3d buffer mode hr=0x%x", hr );
return DVERR_GENERIC;
}
if( dwMode != DS3DMODE_DISABLE )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Already have a buffer for specified user" );
return DVERR_ALREADYBUFFERED;
}
// Check return code
hr = lpds3dTmp->SetMode( DS3DMODE_NORMAL, DS3D_IMMEDIATE );
if( hr != DV_OK )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to set the mode to activate 3d buffer hr=0x%x", hr );
return hr;
}
// Add a reference for the user (core already has one)
m_lpstGeneralBuffer->AddRef();
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Done" );
*lpBuffer = lpds3dTmp;
return DV_OK;
}
LONG lResult;
CSoundTarget *lpstTarget = NULL;
// Check for existing buffer.. if it already exists return it
// (Note: Adds a reference to the buffer)
hr = FindSoundTarget( dvidID, &lpstTarget );
if( hr == DV_OK )
{
if( lpstTarget != NULL )
{
lResult = lpstTarget->Release();
DNASSERT( lResult != 0 );
}
DPFX(DPFPREP, DVF_ERRORLEVEL, "CreateUserBuffer: Find of buffer failed. hr=0x%x", hr );
return DVERR_ALREADYBUFFERED;
}
if( lpstTarget != NULL )
{
lResult = lpstTarget->Release();
DNASSERT( lResult != 0 );
DPFX(DPFPREP, DVF_ERRORLEVEL, "CreateUserBuffer: Buffer already available" );
return DVERR_ALREADYBUFFERED;
}
// If the user has given us a buffer
if( lpdsBuffer )
{
DPFX(DPFPREP, DVF_INFOLEVEL, "Creating buffer using user buffer" );
lpstTarget = new CSoundTarget( dvidID, m_audioPlaybackDevice, lpdsBuffer, (s_lpwfxPlaybackFormat->wBitsPerSample == 8), dwPriority, dwFlags, m_dwUnCompressedFrameSize );
}
else
{
DSBUFFERDESC dsBufferDesc;
DPFX(DPFPREP, DVF_INFOLEVEL, "Creating buffer using user buffer" );
// Fill in appropriate values for the buffer description
SetupPlaybackBufferDesc( &dsBufferDesc, NULL );
// Buffer and sound target ref count = 1
lpstTarget = new CSoundTarget( dvidID, m_audioPlaybackDevice, &dsBufferDesc, dwPriority, dwFlags, m_dwUnCompressedFrameSize );
}
if( lpstTarget == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "CreateUserBuffer: Failed allocating sound target" );
return DVERR_OUTOFMEMORY;
}
hr = lpstTarget->GetInitResult();
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "CreateUserBuffer: Init of buffer failed. hr=0x%x", hr );
lpstTarget->Release();
return hr;
}
hr = lpstTarget->StartMix();
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to start the mix on secondary buffer hr=0x%x.", hr );
lpstTarget->Release();
return hr;
}
// Buffer and sound target ref count = 2
lpstTarget->AddRef();
hr = AddSoundTarget( lpstTarget );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "CreateUserBuffer: AddTarget failed. hr=0x%x", hr );
// Destroy reference from above
lResult = lpstTarget->Release();
DNASSERT( lResult != 0 );
// Destroy base reference
lResult = lpstTarget->Release();
DNASSERT( lResult == 0 );
return hr;
}
*lpBuffer = lpstTarget->Get3DBuffer();
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Done" );
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::Delete3DSoundBuffer"
//
// Delete3DSoundBuffer
//
// Removes the specified ID from the mixer buffer list. Further speech from
// the specified player will be played in the remaining buffer.
//
// Called By:
// - DVC_DeleteUserBuffer
//
// Locks Required:
// - Global Write Lock
//
HRESULT CDirectVoiceClientEngine::Delete3DSoundBuffer( DVID dvidID, LPDIRECTSOUND3DBUFFER *lplpBuffer )
{
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Begin" );
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
DPFX(DPFPREP, DVF_APIPARAM, "Params: dvidID = 0x%x lpBuffer = 0x%p", dvidID, lplpBuffer );
if( lplpBuffer == NULL ||
!DNVALID_WRITEPTR( lplpBuffer, sizeof( LPDIRECTSOUND3DBUFFER ) ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid pointer" );
return E_POINTER;
}
CDVCSLock guardLock(&m_csClassLock);
guardLock.Lock();
if( m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Not initialized" );
return DVERR_NOTINITIALIZED;
}
else if( m_dwCurrentState != DVCSTATE_CONNECTED &&
m_dwCurrentState != DVCSTATE_DISCONNECTING )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Not connected" );
return DVERR_NOTCONNECTED;
}
HRESULT hr;
DWORD dwMode;
LONG lResult;
// Handle request to disable 3D on the main buffer
if( dvidID == DVID_REMAINING )
{
LPDIRECTSOUND3DBUFFER lpTmpBuffer;
lpTmpBuffer = m_lpstGeneralBuffer->Get3DBuffer();
if( lpTmpBuffer == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "No 3d buffer supported" );
return DVERR_NOTBUFFERED;
}
if( lpTmpBuffer != *lplpBuffer )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Buffer passed in does not belong to specified id" );
return DVERR_INVALIDPARAM;
}
hr = lpTmpBuffer->GetMode( &dwMode );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to get current mode hr=0x%x", hr );
return DVERR_GENERIC;
}
if( dwMode == DS3DMODE_DISABLE )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Not buffered" );
return DVERR_NOTBUFFERED;
}
// Check return code
// Add reference
hr = lpTmpBuffer->SetMode( DS3DMODE_DISABLE, DS3D_IMMEDIATE );
if( hr != DV_OK )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to set the mode to activate 3d buffer hr=0x%x", hr );
return DVERR_GENERIC;
}
hr = lpTmpBuffer->SetPosition( 0.0, 0.0, 0.0, DS3D_IMMEDIATE );
// Not a Failure condition.
if( hr != DV_OK )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to set the position of the 3d buffer hr=0x%x", hr );
}
// Remove reference the user has
lResult = m_lpstGeneralBuffer->Release();
*lplpBuffer = NULL;
return DV_OK;
}
CSoundTarget *lpstTarget;
hr = FindSoundTarget( dvidID, &lpstTarget );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "CreateUserBuffer: Find of buffer failed. hr=0x%x", hr );
return DVERR_NOTBUFFERED;
}
if( lpstTarget == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "DeleteUserBuffer: Unable to retrieve user record" );
return DVERR_NOTBUFFERED;
}
if( lpstTarget->Get3DBuffer() != *lplpBuffer )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Buffer passed in does not belong to specified id" );
// Get rid of the reference this func has
lResult = lpstTarget->Release();
DNASSERT( lResult != 0 );
return DVERR_INVALIDPARAM;
}
DPFX(DPFPREP, DVF_SOUNDTARGET_DEBUG_LEVEL, "SOUNDTARGET: [0x%x] DESTROY3DBUFFER ", dvidID );
// Get rid of the reference the FindSoundTarget has
lResult = lpstTarget->Release();
DNASSERT( lResult != 0 );
// Get rid of the reference the user has
lResult = lpstTarget->Release();
DNASSERT( lResult != 0 );
// Destroy the last reference (unless there is one outstanding)
DeleteSoundTarget( dvidID );
*lplpBuffer = NULL;
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Done" );
return DV_OK;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
//
// DIRECTPLAY/NET --> DirectXVoiceClient Interface
//
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::ReceiveSpeechMessage"
// ReceiveSpeechMessage
//
// Called by DirectPlay/DirectNet when a DirectXVoice message is received
//
// Called By:
// - DV_ReceiveSpeechMessage
//
// Locks Required:
// - None
//
BOOL CDirectVoiceClientEngine::ReceiveSpeechMessage( DVID dvidSource, LPVOID lpMessage, DWORD dwSize )
{
BOOL fResult;
PDVPROTOCOLMSG_FULLMESSAGE lpdvFullMessage;
// if we dont' have at least one byte then we are going to bail
if ( dwSize < 1 )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::ReceiveSpeechMessage() Ignoring zero-byte sized message from=0x%x",
dvidSource );
return FALSE;
}
lpdvFullMessage = (PDVPROTOCOLMSG_FULLMESSAGE) lpMessage;
if( !ValidatePacketType( lpdvFullMessage ) )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::ReceiveSpeechMessage() Ignoring message with invalid packet type, type=0x%x, from=0x%x",
lpdvFullMessage->dvGeneric.dwType, dvidSource );
return FALSE;
}
switch( lpdvFullMessage->dvGeneric.dwType )
{
case DVMSGID_HOSTMIGRATELEAVE:
fResult = HandleHostMigrateLeave( dvidSource, static_cast<PDVPROTOCOLMSG_HOSTMIGRATELEAVE>(lpMessage), dwSize );
break;
case DVMSGID_HOSTMIGRATED:
fResult = HandleHostMigrated( dvidSource, static_cast<PDVPROTOCOLMSG_HOSTMIGRATED>(lpMessage),dwSize );
break;
case DVMSGID_CONNECTREFUSE:
fResult = HandleConnectRefuse( dvidSource, static_cast<PDVPROTOCOLMSG_CONNECTREFUSE>(lpMessage), dwSize );
break;
case DVMSGID_CONNECTACCEPT:
fResult = HandleConnectAccept( dvidSource, static_cast<PDVPROTOCOLMSG_CONNECTACCEPT>(lpMessage), dwSize );
break;
case DVMSGID_CREATEVOICEPLAYER:
fResult = HandleCreateVoicePlayer( dvidSource, static_cast<PDVPROTOCOLMSG_PLAYERJOIN>(lpMessage), dwSize );
break;
case DVMSGID_DELETEVOICEPLAYER:
fResult = HandleDeleteVoicePlayer( dvidSource, static_cast<PDVPROTOCOLMSG_PLAYERQUIT>(lpMessage), dwSize );
break;
case DVMSGID_SPEECH:
fResult = HandleSpeech( dvidSource, static_cast<PDVPROTOCOLMSG_SPEECHHEADER>(lpMessage), dwSize );
break;
case DVMSGID_SPEECHBOUNCE:
fResult = HandleSpeechBounce( dvidSource, static_cast<PDVPROTOCOLMSG_SPEECHHEADER>(lpMessage), dwSize );
break;
case DVMSGID_SPEECHWITHFROM:
fResult = HandleSpeechWithFrom( dvidSource, static_cast<PDVPROTOCOLMSG_SPEECHWITHFROM>(lpMessage), dwSize );
break;
case DVMSGID_DISCONNECTCONFIRM:
fResult = HandleDisconnectConfirm( dvidSource, static_cast<PDVPROTOCOLMSG_DISCONNECT>(lpMessage), dwSize);
break;
case DVMSGID_SETTARGETS:
fResult = HandleSetTarget( dvidSource, static_cast<PDVPROTOCOLMSG_SETTARGET>(lpMessage), dwSize );
break;
case DVMSGID_SESSIONLOST:
fResult = HandleSessionLost( dvidSource, static_cast<PDVPROTOCOLMSG_SESSIONLOST>(lpMessage), dwSize );
break;
case DVMSGID_PLAYERLIST:
fResult = HandlePlayerList( dvidSource, static_cast<PDVPROTOCOLMSG_PLAYERLIST>(lpMessage), dwSize );
break;
default:
DPFX(DPFPREP, DVF_WARNINGLEVEL, "DVCE::ReceiveSpeechMessage() Ignoring non-speech message id=0x%x from=0x%x",
lpdvFullMessage->dvGeneric.dwType, dvidSource );
return FALSE;
}
return fResult;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::HandleSetTarget"
// HandleSetTarget
//
// Handles server settarget messages. Sets the local target.
//
BOOL CDirectVoiceClientEngine::HandleSetTarget( DVID dvidSource, PDVPROTOCOLMSG_SETTARGET lpdvSetTarget, DWORD dwSize )
{
HRESULT hr;
// Confirm that server controlled targetting is enabled. If it isn't then this should be ignored.
if( !(m_dvSessionDesc.dwFlags & DVSESSION_SERVERCONTROLTARGET) )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSetTarget() Ignoring target message in session w/o server controlled targetting from=0x%x",
dvidSource );
return FALSE;
}
// Ignore set target message unless it is from the server.
if( dvidSource != m_dvidServer )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSetTarget() Ignoring set target from non-server from=0x%x",
dvidSource );
return FALSE;
}
// check structure size first so that we don't crash by accessing bad data
if ( dwSize < sizeof( DVPROTOCOLMSG_SETTARGET ) || ( dwSize != (sizeof( DVPROTOCOLMSG_SETTARGET ) + ( lpdvSetTarget->dwNumTargets * sizeof(DVID) ) ) ) )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSetTarget() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
dwSize, dvidSource );
return FALSE;
}
if( lpdvSetTarget->dwNumTargets > DV_MAX_TARGETS )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSetTarget() Ignoring message with too many targets, targets=0x%x, from=0x%x",
lpdvSetTarget->dwNumTargets, dvidSource );
return FALSE;
}
hr = InternalSetTransmitTarget( (DWORD *) &lpdvSetTarget[1], lpdvSetTarget->dwNumTargets );
DNASSERT( hr == DV_OK );
return TRUE;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::HandleHostMigrateLeave"
BOOL CDirectVoiceClientEngine::HandleHostMigrateLeave( DVID dvidSource, PDVPROTOCOLMSG_HOSTMIGRATELEAVE lpdvHostMigrateLeave, DWORD dwSize )
{
// Call RemovePlayer with the ID of the person who sent this,
// which should be the host.
DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Triggered by DVMSGID_HOSTMIGRATELEAVE" );
MigrateHost_RunElection();
return TRUE;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::HandleHostMigrated"
BOOL CDirectVoiceClientEngine::HandleHostMigrated( DVID dvidSource, PDVPROTOCOLMSG_HOSTMIGRATED lpdvHostMigrated, DWORD dwSize )
{
HRESULT hr;
CDVCSLock guardLock(&m_csClassLock);
guardLock.Lock();
// We're not yet connected, so we can't send our settings to the server yet.
// However, because of the write lock we know that when the connection
// completes we'll have the right host. (Transparently to the client).
//
// We need to proceed if we're disconnecting because we need to send a new disconnect confirm
//
if( m_dwCurrentState != DVCSTATE_CONNECTED &&
m_dwCurrentState != DVCSTATE_DISCONNECTING )
{
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Could not respond to new host yet, not yet initialized" );
return TRUE;
}
if( dvidSource != m_dvidServer )
{
DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Ignoring host migration from 0x%x -- 0x%x is server", dvidSource, m_dvidServer );
return TRUE;
}
guardLock.Unlock();
if( m_dwCurrentState == DVCSTATE_DISCONNECTING )
{
DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Sending NEW host disconnect request" );
hr = Send_DisconnectRequest();
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Internal send failed hr=0x%x", hr );
}
else
{
hr=DV_OK;
}
}
else
{
DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Player 0x%x received host migrated message!", m_dvidLocal );
DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Sending player confirm message" );
DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: According to message Player 0x%x is new host", dvidSource );
hr = Send_SettingsConfirm();
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Internal send failed hr=0x%x", hr );
}
else
{
hr=DV_OK;
}
}
return TRUE;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::HandleConnectRefuse"
// HandleConnectRefuse
//
// Handles connection refusals
//
BOOL CDirectVoiceClientEngine::HandleConnectRefuse( DVID dvidSource, PDVPROTOCOLMSG_CONNECTREFUSE lpdvConnectRefuse, DWORD dwSize )
{
if ( dwSize != sizeof( DVPROTOCOLMSG_CONNECTREFUSE ) )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleConnectRefuse() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
dwSize, dvidSource );
return FALSE;
}
// This prevents someone from sending a connect refuse to bump off a valid player
if( GetCurrentState() != DVCSTATE_CONNECTING )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleConnectRefuse() Ignoring connect refuse AFTER connection from=0x%x", dvidSource );
return FALSE;
}
// Prevent processing of this message unless it came from the server
if( dvidSource != m_dvidServer )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleConnectRefuse() Ignoring connect refuse from non-server - from=0x%x", dvidSource );
return FALSE;
}
// Do some brain dead error checking. Should never happen but print
// some debug spew just in case
if (SUCCEEDED(lpdvConnectRefuse->hresResult))
{
DPFX(DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "CDirectVoiceClientEngine::HandleConnectRefuse but reason given is success!" );
}
SetConnectResult( lpdvConnectRefuse->hresResult );
SetEvent( m_hNotifyConnect );
return TRUE;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::DeInitializeClientServer"
// DeInitializeClientServer
//
// This function is responsible for pre-allocating the information
// for receiving client/server voice.
//
void CDirectVoiceClientEngine::DeInitializeClientServer()
{
DVPROTOCOLMSG_PLAYERQUIT dvPlayerQuit;
dvPlayerQuit.dwType = DVMSGID_DELETEVOICEPLAYER;
dvPlayerQuit.dvidID = m_dvidServer;
HandleDeleteVoicePlayer( 0, &dvPlayerQuit, sizeof( DVPROTOCOLMSG_PLAYERQUIT ) );
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::InitializeClientServer"
// InitializeClientServer
//
// This function is responsible for pre-allocating the information
// for receiving client/server voice
//
HRESULT CDirectVoiceClientEngine::InitializeClientServer()
{
DPFX(DPFPREP, DVF_INFOLEVEL, "DVCE::InitializeClientServer() - Initializing Client/Server Queues" );
HRESULT hr;
CVoicePlayer *pNewPlayer;
QUEUE_PARAMS queueParams;
pNewPlayer = (CVoicePlayer*)m_fpPlayers.Get();
if( pNewPlayer == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to allocate new player for client/server" );
return DVERR_OUTOFMEMORY;
}
ASSERT_VPLAYER(pNewPlayer);
hr = pNewPlayer->Initialize( m_dvidServer, 0, 0, NULL, &m_fpPlayers );
if( FAILED( hr ) )
{
pNewPlayer->Release();
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to initialize server player record" );
return hr;
}
queueParams.wFrameSize = m_dwCompressedFrameSize;
queueParams.bInnerQueueSize = m_lpdvfCompressionInfo->wInnerQueueSize;
queueParams.bMaxHighWaterMark = m_lpdvfCompressionInfo->wMaxHighWaterMark,
queueParams.iQuality = m_dvClientConfig.dwBufferQuality;
queueParams.iHops = 2;
queueParams.iAggr = m_dvClientConfig.dwBufferAggressiveness;
queueParams.bInitHighWaterMark = 2;
queueParams.wQueueId = -1;
queueParams.wMSPerFrame = m_lpdvfCompressionInfo->dwTimeout,
queueParams.pFramePool = m_pFramePool;
hr = pNewPlayer->CreateQueue( &queueParams );
if( FAILED( hr ) )
{
pNewPlayer->Release();
DPFX(DPFPREP, DVF_ERRORLEVEL, "Could not create queue hr=0x%x", hr );
return hr;
}
hr = m_voiceNameTable.AddEntry( m_dvidServer, pNewPlayer );
if( FAILED( hr ) )
{
// Main ref
pNewPlayer->Release();
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to add entry to nametable hr=0x%x", hr );
return TRUE;
}
// Add a reference for the player to the "Playback Add List"
DNEnterCriticalSection( &m_csPlayAddList );
pNewPlayer->AddRef();
pNewPlayer->AddToPlayList( &m_blPlayAddPlayers );
DNLeaveCriticalSection( &m_csPlayAddList );
// Add a reference for the player to the "Notify Add List"
DNEnterCriticalSection( &m_csNotifyAddList );
pNewPlayer->AddRef();
pNewPlayer->AddToNotifyList( &m_blNotifyAddPlayers );
DNLeaveCriticalSection( &m_csNotifyAddList );
pNewPlayer->SetAvailable( TRUE );
m_fLocalPlayerAvailable = TRUE;
// Release our personal reference
pNewPlayer->Release();
DPFX(DPFPREP, DVF_INFOLEVEL, "DVCE::InitializeClientServer() - Done Initializing Client/Server Queues" );
return DP_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::DoConnectResponse"
void CDirectVoiceClientEngine::DoConnectResponse()
{
HRESULT hr = DV_OK;
CDVCSLock guardLock(&m_csClassLock);
guardLock.Lock();
if( m_dwCurrentState != DVCSTATE_CONNECTING )
{
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Aborting Connection & server response arrived" );
return;
}
// Work from the default assumption that something will screw up
SetConnectResult( DVERR_GENERIC );
ClientStats_Begin();
#ifndef __DISABLE_SOUND
// Handle sound initialization
hr = InitializeSoundSystem();
if( FAILED(hr) )
{
SetConnectResult(hr);
DPFX(DPFPREP, DVF_ERRORLEVEL, "Sound Initialization Failed hr=0x%x", hr );
goto EXIT_ERROR;
}
#endif
hr = SetupSpeechBuffer();
if( FAILED( hr ) )
{
SetConnectResult(hr);
DPFX(DPFPREP, DVF_ERRORLEVEL, "Could not init speech buffers hr=0x%x", hr );
goto EXIT_ERROR;
}
DPFX(DPFPREP, DVF_INFOLEVEL, "DVCE::HandleConnectAccept() - Sound Initialized" );
// If we're running in client/server we need to pre-create a single buffer
// and compressor
if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_MIXING ||
m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_ECHO )
{
hr = InitializeClientServer();
if( FAILED( hr ) )
{
SetConnectResult(hr);
DPFX(DPFPREP, DVF_ERRORLEVEL, "Could not init client/server hr=0x%x", hr );
goto EXIT_ERROR;
}
}
// We need to make player available in client/server because there will be no indication
if( m_dvSessionDesc.dwSessionType != DVSESSIONTYPE_PEER )
{
m_fLocalPlayerAvailable = TRUE;
}
#ifndef __DISABLE_SOUND
// Start playback thread
// Create Thread events
m_hPlaybackTerminate = CreateEvent( NULL, FALSE, FALSE, NULL );
m_hPlaybackDone = CreateEvent( NULL, FALSE, FALSE, NULL );
// Create Semaphores for playback and record
m_thTimerInfo.hPlaybackTimerEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
m_thTimerInfo.lPlaybackCount = 0;
m_thTimerInfo.hRecordTimerEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
if( m_hPlaybackTerminate == NULL || m_hPlaybackDone == NULL ||
m_thTimerInfo.hPlaybackTimerEvent == NULL || m_thTimerInfo.hRecordTimerEvent == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Windows error, event create failure." );
SetConnectResult( DVERR_GENERIC );
goto EXIT_ERROR;
}
// Create Multimedia timer
m_pTimer = new DvTimer;
if( m_pTimer == NULL )
{
SetConnectResult( DVERR_OUTOFMEMORY );
goto EXIT_ERROR;
}
if( !m_pTimer->Create( m_lpdvfCompressionInfo->dwTimeout / DV_CLIENT_WAKEUP_MULTIPLER,
&m_thTimerInfo, MixingWakeupProc ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to create timer." );
SetConnectResult( DVERR_GENERIC );
goto EXIT_ERROR;
}
m_hPlaybackThreadHandle = (HANDLE) _beginthread( PlaybackThread, 0, this );
#ifdef __CORE_THREAD_PRIORITY_HIGH
SetThreadPriority( m_hPlaybackThreadHandle, THREAD_PRIORITY_TIME_CRITICAL );
#endif
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
DPFX(DPFPREP, DVF_INFOLEVEL, "DVCE::HandleConnectAccept() - Playback Thread Started: 0x%p", m_hPlaybackThreadHandle );
if( !(m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_HALFDUPLEX) )
{
m_hRecordTerminate = CreateEvent( NULL, FALSE, FALSE, NULL );
m_hRecordDone = CreateEvent( NULL, FALSE, FALSE, NULL );
if (m_hRecordTerminate==NULL || m_hRecordDone==NULL)
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Windows error, event create failure. hr=0x%x", GetLastError() );
SetConnectResult( DVERR_GENERIC );
goto EXIT_ERROR;
}
// Start Record Thread
m_hRecordThreadHandle = (HANDLE) _beginthread( RecordThread, 0, this );
#ifdef __CORE_THREAD_PRIORITY_HIGH
SetThreadPriority( m_hRecordThreadHandle, THREAD_PRIORITY_TIME_CRITICAL );
#endif
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
DPFX(DPFPREP, DVF_INFOLEVEL, "DVCE::HandleConnectAccept() - Record Thread Started: 0x%p", m_hRecordThreadHandle );
}
else
{
m_hRecordTerminate = NULL;
m_hRecordDone = NULL;
}
#endif
SetCurrentState( DVCSTATE_CONNECTED );
SetConnectResult(DV_OK);
DPFX(DPFPREP, DVF_ENTRYLEVEL, "DVCE::HandleConnectAccept() Success" );
SendConnectResult();
///////
guardLock.Unlock();
hr = Send_SettingsConfirm();
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to send connect confirmation hr=0x%x", hr );
DPFX(DPFPREP, DVF_ERRORLEVEL, "Other threads will cleanup because connection must be gone" );
}
SetEvent( m_hConnectAck );
return;
EXIT_ERROR:
if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_MIXING ||
m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_ECHO )
{
DeInitializeClientServer();
}
DPFX(DPFPREP, DVF_ERRORLEVEL, "HandleConnectAccept Failed hr=0x%x", GetConnectResult() );
DV_DUMP_GUID( m_dvSessionDesc.guidCT );
guardLock.Unlock();
Cleanup();
SendConnectResult();
SetEvent( m_hConnectAck );
return;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::HandleConnectAccept"
// HandleConnectAccepts
//
// Handles connect accepts. Sets connected flag, finishes initialization, informs the
// connect function to proceed (if it's waiting).
//
BOOL CDirectVoiceClientEngine::HandleConnectAccept( DVID dvidSource, PDVPROTOCOLMSG_CONNECTACCEPT lpdvConnectAccept, DWORD dwSize )
{
char tmpString[100];
if ( dwSize != sizeof( DVPROTOCOLMSG_CONNECTACCEPT ) )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleConnectAccept() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
dwSize, dvidSource );
return FALSE;
}
if( !ValidateSessionType( lpdvConnectAccept->dwSessionType ) )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleConnectAccept() Ignoring message with invalid session type, type=0x%x, from=0x%x",
lpdvConnectAccept->dwSessionType, dvidSource );
return FALSE;
}
if( !ValidateSessionFlags( lpdvConnectAccept->dwSessionFlags ) )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleConnectAccept() Ignoring message with invalid session flags, flags=0x%x, from=0x%x",
lpdvConnectAccept->dwSessionFlags, dvidSource );
return FALSE;
}
DPFX(DPFPREP, DVF_ENTRYLEVEL, "DVCE::HandleConnectAccept() Entry" );
CDVCSLock guardLock(&m_csClassLock);
guardLock.Lock();
// We're already connected, server is responding to the earlier request
if( m_dwCurrentState != DVCSTATE_CONNECTING )
{
return TRUE;
}
m_hPlaybackTerminate = NULL;
m_hPlaybackDone = NULL;
m_thTimerInfo.hPlaybackTimerEvent = NULL;
m_thTimerInfo.hRecordTimerEvent = NULL;
m_pTimer = NULL;
// Inform transport layer who the server is. (So it no longer thinks it's DPID_ALL).
m_lpSessionTransport->MigrateHost( dvidSource );
m_dvidServer = m_lpSessionTransport->GetServerID();
DVPROTOCOLMSG_FULLMESSAGE dvMessage;
DPFX(DPFPREP, DVF_INFOLEVEL, "Connect Accept Received" );
m_dvSessionDesc.dwSize = sizeof( DVSESSIONDESC );
m_dvSessionDesc.dwBufferAggressiveness = 0;
m_dvSessionDesc.dwBufferQuality = 0;
m_dvSessionDesc.dwFlags = lpdvConnectAccept->dwSessionFlags;
m_dvSessionDesc.guidCT = lpdvConnectAccept->guidCT;
m_dvSessionDesc.dwSessionType = lpdvConnectAccept->dwSessionType;
HRESULT hr = DVCDB_GetCompressionInfo( m_dvSessionDesc.guidCT, &m_lpdvfCompressionInfo );
if( FAILED( hr ) )
{
SetConnectResult( DVERR_COMPRESSIONNOTSUPPORTED );
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid Compression Type" );
SetEvent( m_hNotifyConnect );
return TRUE;
}
SetConnectResult( DV_OK );
DV_DUMP_CIF( m_lpdvfCompressionInfo, 1 );
SetEvent( m_hNotifyConnect );
return TRUE;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::HandlePlayerList"
BOOL CDirectVoiceClientEngine::HandlePlayerList( DVID dvidSource, PDVPROTOCOLMSG_PLAYERLIST lpdvPlayerList, DWORD dwSize )
{
DVPROTOCOLMSG_PLAYERJOIN dvMsgPlayerJoin; // Used to fake out HandleCreateVoicePlayer
DWORD dwIndex;
// Prevent player list path in non-peer-to-peer sessions
if( m_dvSessionDesc.dwSessionType != DVSESSIONTYPE_PEER )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "Ignoring playerlist in non-peer session from=0x%x", dvidSource );
return FALSE;
}
// Prevent player list from non-server.
if( dvidSource != m_dvidServer )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "Ignoring player list from non-host - from=0x%x", dvidSource );
return FALSE;
}
// check structure size first so that we don't crash by accessing bad data
if ( dwSize < sizeof( DVPROTOCOLMSG_PLAYERLIST ) || ( dwSize != (sizeof( DVPROTOCOLMSG_PLAYERLIST ) + ( lpdvPlayerList->dwNumEntries * sizeof(DVPROTOCOLMSG_PLAYERLIST_ENTRY ) ) ) ) )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandlePlayerList() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
dwSize, dvidSource );
return FALSE;
}
// Should transition this from -1 to 0 if this is the first time, otherwise it will be positive
if( InterlockedIncrement( &m_iPlayerListReceived ) != 0 )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "Ignoring dup player list - from=0x%x", dvidSource );
return FALSE;
}
const DVPROTOCOLMSG_PLAYERLIST_ENTRY *pdvPlayerList = (DVPROTOCOLMSG_PLAYERLIST_ENTRY *) &lpdvPlayerList[1];
// Get our host order ID
m_dwHostOrderID = lpdvPlayerList->dwHostOrderID;
DPFX(DPFPREP, DVF_INFOLEVEL, "Received player list. Unpacking %d entries", lpdvPlayerList->dwNumEntries );
for( dwIndex = 0; dwIndex < lpdvPlayerList->dwNumEntries; dwIndex++ )
{
dvMsgPlayerJoin.dwType = DVMSGID_CREATEVOICEPLAYER;
dvMsgPlayerJoin.dvidID = pdvPlayerList[dwIndex].dvidID;
dvMsgPlayerJoin.dwFlags = pdvPlayerList[dwIndex].dwPlayerFlags;
dvMsgPlayerJoin.dwHostOrderID = pdvPlayerList[dwIndex].dwHostOrderID;
if( !InternalCreateVoicePlayer( dvidSource, &dvMsgPlayerJoin, sizeof( DVPROTOCOLMSG_PLAYERJOIN ) ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Handle voice player failed during unpack" );
return FALSE;
}
}
return TRUE;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::NotifyComplete_SyncWait"
//
// NotifyComplete_SyncWait
//
// This is a completion function for notifications which need to be performed synchronously.
//
void CDirectVoiceClientEngine::NotifyComplete_SyncWait( PVOID pvContext, CNotifyElement *pElement )
{
HANDLE *pTmpHandle = (HANDLE *) pvContext;
DNASSERT( pTmpHandle != NULL );
if( pTmpHandle != NULL && *pTmpHandle != NULL )
{
SetEvent( *pTmpHandle );
}
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::NotifyComplete_RemotePlayer"
//
// NotifyComplete_RemotePlayer
//
// This is a completion function for when notification of a new remote player has been processed
//
void CDirectVoiceClientEngine::NotifyComplete_RemotePlayer( PVOID pvContext, CNotifyElement *pElement )
{
CVoicePlayer *pPlayer = (CVoicePlayer *) pvContext;
ASSERT_VPLAYER(pPlayer);
PDVMSG_CREATEVOICEPLAYER pCreatePlayer = NULL;
if( pElement->m_etElementType == NOTIFY_DYNAMIC )
{
pCreatePlayer = (PDVMSG_CREATEVOICEPLAYER) pElement->m_element.dynamic.m_lpData;
}
else
{
pCreatePlayer = (PDVMSG_CREATEVOICEPLAYER) pElement->m_element.fixed.m_bFixedHolder;
}
DNASSERT( pPlayer != NULL );
DNASSERT( pCreatePlayer->dwSize == sizeof( DVMSG_CREATEVOICEPLAYER ) );
pPlayer->SetContext( pCreatePlayer->pvPlayerContext );
pPlayer->SetAvailable( TRUE );
pPlayer->Release();
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::NotifyComplete_LocalPlayer"
//
// NotifyComplete_LocalPlayer
//
// This is a completion function for when notification of the local player has been processed
//
void CDirectVoiceClientEngine::NotifyComplete_LocalPlayer( PVOID pvContext, CNotifyElement *pElement )
{
CDirectVoiceClientEngine *pvEngine = (CDirectVoiceClientEngine *) pvContext;
PDVMSG_CREATEVOICEPLAYER pCreatePlayer = NULL;
if( pElement->m_etElementType == NOTIFY_DYNAMIC )
{
pCreatePlayer = (PDVMSG_CREATEVOICEPLAYER) pElement->m_element.dynamic.m_lpData;
}
else
{
pCreatePlayer = (PDVMSG_CREATEVOICEPLAYER) pElement->m_element.fixed.m_bFixedHolder;
}
DNASSERT( pCreatePlayer->dwSize == sizeof( DVMSG_CREATEVOICEPLAYER ) );
pvEngine->m_pvLocalPlayerContext = pCreatePlayer->pvPlayerContext;
pvEngine->m_fLocalPlayerAvailable = TRUE;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::HandleCreateVoicePlayer"
//
// HandleCreateVoicePlayer
//
// Validates createvoice player message, source and session type for incoming createvoiceplayer messages.
// If the message is valid, it gets processed.
//
BOOL CDirectVoiceClientEngine::HandleCreateVoicePlayer( DVID dvidSource, PDVPROTOCOLMSG_PLAYERJOIN lpdvCreatePlayer, DWORD dwSize )
{
if ( dwSize != sizeof( DVPROTOCOLMSG_PLAYERJOIN ) )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleCreateVoicePlayer() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
dwSize, dvidSource );
return FALSE;
}
// Prevent createplayer path in non-peer-to-peer sessions
if( m_dvSessionDesc.dwSessionType != DVSESSIONTYPE_PEER )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleCreateVoicePlayer() Ignoring create player in non-peer session from=0x%x", dvidSource );
return FALSE;
}
// Prevent createplayer from non-server.
if( dvidSource != m_dvidServer )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleCreateVoicePlayer() Ignoring create player from non-host - from=0x%x", dvidSource );
return FALSE;
}
return InternalCreateVoicePlayer( dvidSource, lpdvCreatePlayer, dwSize );
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::HandleCreateVoicePlayer"
//
// InternalCreateVoicePlayer
//
// Performs initialization required to create the specified user's record. This function does very little validity checking,
// the CALLER should do this. This should not be called directly from the wire unless the source and state have
// been validated.
//
// Players in the system will normall have a reference count of 3:
// - 1 for playback thread
// - 1 for notify thread
// - 1 for nametable
//
// When a player is added they are added to the nametable as well as the
// pending lists for notify thread and playback thread.
//
// Both of these threads wakeup and:
// - Add any players on the "add list"
// - Remove any players who are marked disconnecting
//
BOOL CDirectVoiceClientEngine::InternalCreateVoicePlayer( DVID dvidSource, PDVPROTOCOLMSG_PLAYERJOIN lpdvCreatePlayer, DWORD dwSize )
{
if( !ValidatePlayerFlags( lpdvCreatePlayer->dwFlags ) )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleCreateVoicePlayer() Ignoring message with invalid player flags, flags=0x%x, from=0x%x",
lpdvCreatePlayer->dwFlags, dvidSource );
return FALSE;
}
if( !ValidatePlayerDVID( lpdvCreatePlayer->dvidID ) )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleCreateVoicePlayer() Ignoring message with invalid player dvid, flags=0x%x, from=0x%x",
lpdvCreatePlayer->dvidID, dvidSource );
return FALSE;
}
if( m_dwCurrentState != DVCSTATE_CONNECTED )
return TRUE;
CVoicePlayer *newPlayer;
HRESULT hr;
QUEUE_PARAMS queueParams;
hr = m_voiceNameTable.GetEntry( lpdvCreatePlayer->dvidID, &newPlayer, TRUE );
// Ignore duplicate players
if( hr == DV_OK )
{
ASSERT_VPLAYER(newPlayer);
newPlayer->Release();
return TRUE;
}
DPFX(DPFPREP, DVF_CONNECT_PROCEDURE_DEBUG_LEVEL, "Received Create for player ID 0x%x",lpdvCreatePlayer->dvidID );
// Do not both creating a queue or a player entry for ourselves
// Not needed
if( lpdvCreatePlayer->dvidID != m_lpSessionTransport->GetLocalID() )
{
DPFX(DPFPREP, DVF_CONNECT_PROCEDURE_DEBUG_LEVEL, "Creating player record" );
newPlayer = (CVoicePlayer*)m_fpPlayers.Get();
if( newPlayer == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to allocate new player record. Alloc failure" );
return TRUE;
}
ASSERT_VPLAYER(newPlayer);
hr = newPlayer->Initialize( lpdvCreatePlayer->dvidID, lpdvCreatePlayer->dwHostOrderID, lpdvCreatePlayer->dwFlags, NULL, &m_fpPlayers );
if( FAILED( hr ) )
{
newPlayer->Release();
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to initialize player record hr=0x%x", hr );
return TRUE;
}
queueParams.wFrameSize = m_dwCompressedFrameSize;
queueParams.bInnerQueueSize = m_lpdvfCompressionInfo->wInnerQueueSize;
queueParams.bMaxHighWaterMark = m_lpdvfCompressionInfo->wMaxHighWaterMark,
queueParams.iQuality = m_dvClientConfig.dwBufferQuality;
queueParams.iHops = 1;
queueParams.iAggr = m_dvClientConfig.dwBufferAggressiveness;
queueParams.bInitHighWaterMark = 2;
queueParams.wQueueId = lpdvCreatePlayer->dvidID;
queueParams.wMSPerFrame = m_lpdvfCompressionInfo->dwTimeout,
queueParams.pFramePool = m_pFramePool;
hr = newPlayer->CreateQueue( &queueParams );
if( FAILED( hr ) )
{
newPlayer->Release();
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to create queue for player hr=0x%x", hr );
return TRUE;
}
hr = m_voiceNameTable.AddEntry( lpdvCreatePlayer->dvidID, newPlayer );
if( FAILED( hr ) )
{
// Main ref
newPlayer->Release();
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to add entry to nametable hr=0x%x", hr );
return TRUE;
}
if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_FORWARDING )
{
newPlayer->SetAvailable(TRUE);
}
// Add a reference for the player to the "Playback Add List"
DNEnterCriticalSection( &m_csPlayAddList );
newPlayer->AddRef();
newPlayer->AddToPlayList( &m_blPlayAddPlayers );
DNLeaveCriticalSection( &m_csPlayAddList );
// Add a reference for the player to the "Notify Add List"
DNEnterCriticalSection( &m_csNotifyAddList );
newPlayer->AddRef();
newPlayer->AddToNotifyList( &m_blNotifyAddPlayers );
DNLeaveCriticalSection( &m_csNotifyAddList );
// This will now be released by the callback, unless this is multicast
//
// Release our personal reference
if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_FORWARDING )
{
newPlayer->Release();
}
}
else
{
DPFX(DPFPREP, DVF_CONNECT_PROCEDURE_DEBUG_LEVEL, "Local player, no player record required" );
}
if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_PEER )
{
DVMSG_CREATEVOICEPLAYER dvCreatePlayer;
dvCreatePlayer.dvidPlayer = lpdvCreatePlayer->dvidID;
dvCreatePlayer.dwFlags = lpdvCreatePlayer->dwFlags;
dvCreatePlayer.dwSize = sizeof( DVMSG_CREATEVOICEPLAYER );
dvCreatePlayer.pvPlayerContext = NULL;
// Prevents double notification for local player
if( lpdvCreatePlayer->dvidID != m_lpSessionTransport->GetLocalID() )
{
NotifyQueue_Add( DVMSGID_CREATEVOICEPLAYER, &dvCreatePlayer, sizeof( DVMSG_CREATEVOICEPLAYER ),
newPlayer, NotifyComplete_RemotePlayer );
}
else if( !m_fLocalPlayerNotify )
{
// Add local player flag to notification
dvCreatePlayer.dwFlags |= DVPLAYERCAPS_LOCAL;
// Notify of local player (don't create player record)
NotifyQueue_Add( DVMSGID_CREATEVOICEPLAYER, &dvCreatePlayer, sizeof( DVMSG_CREATEVOICEPLAYER ),
this, NotifyComplete_LocalPlayer );
m_fLocalPlayerNotify = TRUE;
}
}
return TRUE;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::MigrateHost_RunElection"
//
// MigrateHost_RunElection
//
// Runs the host migration algorithm
//
HRESULT CDirectVoiceClientEngine::MigrateHost_RunElection( )
{
HRESULT hr;
if( m_dwCurrentState == DVCSTATE_DISCONNECTING )
{
DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: We're disconnecting, no need to run algorithm" );
return TRUE;
}
// This shortcut prevents problems if this is called twice.
// But only if we're lucky enough this got set before this poin
// We also have to guard against this case in HostMigrateCreate
//
//
if( m_lpdvServerMigrated != NULL )
{
DPFX(DPFPREP, DVF_INFOLEVEL, "Skipping calling removeplayer again as host already migrated" );
return DV_OK;
}
DWORD dwHostOrderID = DVPROTOCOL_HOSTORDER_INVALID;
DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Player 0x%x is running election algorithm", this->m_dvidLocal );
// Prevent double-create from host migration run. This can be called by removeplayer and by hostmigrateleave
// message.
CDVCSLock m_guardLock(&m_csClassLock);
m_guardLock.Lock();
// Trust me. Do this.
DVID dvidNewHost = m_dvidLocal;
// Check everyone else in the session, see who has lowest host order ID
dwHostOrderID = m_voiceNameTable.GetLowestHostOrderID(&dvidNewHost);
// We're lower then everyone else.. We should become new host
if( m_dwHostOrderID <= dwHostOrderID )
{
DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: We're to become the new host" );
dvidNewHost = m_dvidLocal;
}
DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: New Host is [0x%x] OrderID [0x%x] Current [0x%x]", dvidNewHost, dwHostOrderID, m_dwMigrateHostOrderID );
if( m_dwMigrateHostOrderID != DVPROTOCOL_HOSTORDER_INVALID && dwHostOrderID <= m_dwMigrateHostOrderID )
{
DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: No need to run algorithm again -- in progress (0x%x)", dvidNewHost );
m_guardLock.Unlock();
return DV_OK;
}
m_dwMigrateHostOrderID = dwHostOrderID;
m_lpSessionTransport->MigrateHost( dvidNewHost );
m_dvidServer = m_lpSessionTransport->GetServerID();
DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Host is (2) [0x%x]", m_dvidServer );
m_guardLock.Unlock();
// No one was found and we're not properly connected yet
if( dwHostOrderID == DVPROTOCOL_HOSTORDER_INVALID && m_dwCurrentState != DVCSTATE_CONNECTED )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "There is no one to take over session. Disconnect" );
// We're connecting.. expected behaviour
if( m_dwCurrentState == DVCSTATE_CONNECTING )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Aborting connection..." );
SendConnectResult();
SetEvent( m_hConnectAck );
DoSessionLost(DVERR_SESSIONLOST);
}
// We're already disconnecting
else if( m_dwCurrentState == DVCSTATE_DISCONNECTING )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Already disconnecting.." );
}
}
// Candidate was found, it's us!
else if( m_dwHostOrderID <= dwHostOrderID )
{
// Reset the player list received flag to -1 to allow another playerlist message
InterlockedExchange( &m_iPlayerListReceived, -1 );
DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: We think we're the host! Our Host Order ID=0x%x", m_dwHostOrderID );
hr = HandleLocalHostMigrateCreate();
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Host migrate failed hr=0x%x", hr );
}
}
// Someone was elected -- not us!
//
// We send a settings confirm message in case we ignored host migration message
// during our election. (Small, but reproducable window).
//
// If we do get it after this host may get > 1 settings confirm from us (which is handled).
//
else if( dwHostOrderID != DVPROTOCOL_HOSTORDER_INVALID )
{
// Reset the player list received flag to -1 to allow another playerlist message
InterlockedExchange( &m_iPlayerListReceived, -1 );
hr = Send_SettingsConfirm();
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Settings confirm message failed! sent hr=0x%x", hr );
}
DVMSG_HOSTMIGRATED dvHostMigrated;
dvHostMigrated.dvidNewHostID = m_dvidServer;
dvHostMigrated.pdvServerInterface = NULL;
dvHostMigrated.dwSize = sizeof( DVMSG_HOSTMIGRATED );
DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Sending notification because of election" );
NotifyQueue_Add( DVMSGID_HOSTMIGRATED, &dvHostMigrated, sizeof( DVMSG_HOSTMIGRATED ) );
}
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::HandleDeleteVoicePlayer"
//
// HandleDeleteVoicePlayer
//
// Handles the DVMSGID_DELETEVOICEPLAYER message.
//
BOOL CDirectVoiceClientEngine::HandleDeleteVoicePlayer( DVID dvidSource, PDVPROTOCOLMSG_PLAYERQUIT lpdvDeletePlayer, DWORD dwSize )
{
CVoicePlayer *pPlayer;
HRESULT hr;
if ( dwSize != sizeof( DVPROTOCOLMSG_PLAYERQUIT ) )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleDeleteVoicePlayer() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
dwSize, dvidSource );
return FALSE;
}
hr = m_voiceNameTable.GetEntry( lpdvDeletePlayer->dvidID, &pPlayer, TRUE );
// If there is a player entry for the given ID,
// Handle removing them from the local player table
if( pPlayer != NULL )
{
ASSERT_VPLAYER(pPlayer);
// Remove the entry, this will also drop the reference count
hr = m_voiceNameTable.DeleteEntry( lpdvDeletePlayer->dvidID );
// Another thread has already remove the player -- we don't need to do the rest
// of this.
if( FAILED( hr ) )
{
DPFX(DPFPREP, 0, "Error, could not find entry 0x%x to delete hr=0x%x", dvidSource, hr );
pPlayer->Release();
return TRUE;
}
// Mark player record as disconnected
pPlayer->SetDisconnected();
// Wait for global object lock and then remove target
// CDVCSLock guardLock(&m_csClassLock);
// guardLock.Lock();
CheckForAndRemoveTarget( lpdvDeletePlayer->dvidID );
// guardLock.Unlock();
// If there are any buffers for this player, delete them
// We don't need to destroy them, we want to save them so the user can call
// Delete3DUserBuffer
//
//DeleteSoundTarget( lpdvDeletePlayer->dvidID );
if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_PEER )
{
DVMSG_DELETEVOICEPLAYER dvMsgDeletePlayer;
// Event for doing sync wait
HANDLE hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
dvMsgDeletePlayer.dvidPlayer = lpdvDeletePlayer->dvidID;
dvMsgDeletePlayer.dwSize = sizeof( DVMSG_DELETEVOICEPLAYER );
dvMsgDeletePlayer.pvPlayerContext = pPlayer->GetContext();
pPlayer->SetContext( NULL );
// By making this synchronous we ensure that the voice notification has completed before the dplay8
// callback is called.
//
NotifyQueue_Add( DVMSGID_DELETEVOICEPLAYER, &dvMsgDeletePlayer, sizeof( DVMSG_DELETEVOICEPLAYER ), &hEvent, NotifyComplete_SyncWait );
if( hEvent )
{
WaitForSingleObject( hEvent, INFINITE );
CloseHandle( hEvent );
}
}
pPlayer->Release();
}
return TRUE;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::QueueSpeech"
//
// QueueSpeech
//
// Process and queue incoming audio
//
BOOL CDirectVoiceClientEngine::QueueSpeech( DVID dvidSource, PDVPROTOCOLMSG_SPEECHHEADER pdvSpeechHeader, PBYTE pbData, DWORD dwSize )
{
CVoicePlayer *pPlayerInfo;
HRESULT hr;
// Only start receiving voice if the local player is active
if( !m_fLocalPlayerAvailable )
{
DPFX(DPFPREP, 1, "Ignoring incoming audio, local player has not been indicated" );
return TRUE;
}
hr = m_voiceNameTable.GetEntry( dvidSource, &pPlayerInfo, TRUE );
if( FAILED( hr ) )
{
DPFX(DPFPREP, 1, "Received speech for player who is not in nametable hr=0x%x", hr );
return TRUE;
}
ASSERT_VPLAYER(pPlayerInfo);
if( !pPlayerInfo->IsAvailable() )
{
DPFX(DPFPREP, 1, "Player is not yet available, ignoring speech" );
}
else
{
hr = pPlayerInfo->HandleReceive( pdvSpeechHeader, pbData, dwSize );
if( FAILED( hr ) )
{
pPlayerInfo->Release();
DPFX(DPFPREP, 1, "Received speech could not be buffered hr=0x%x", hr );
return TRUE;
}
// STATSBLOCK: Begin
m_pStatsBlob->m_dwPRESpeech++;
// STATSBLOCK: End
}
// Release our reference to the player
pPlayerInfo->Release();
return TRUE;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::HandleSpeechWithFrom"
//
// HandleSpeech
//
// Handles speech data messages
//
BOOL CDirectVoiceClientEngine::HandleSpeechWithFrom( DVID dvidSource, PDVPROTOCOLMSG_SPEECHWITHFROM lpdvSpeech, DWORD dwSize )
{
HRESULT hr;
// Only valid in forwarding sessions
if( m_dvSessionDesc.dwSessionType != DVSESSIONTYPE_FORWARDING )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSpeechWithFrom() Ignoring fwd speech message in non-fwd session - from=0x%x", dvidSource );
return FALSE;
}
// This messages can only come from the server.
if( dvidSource != m_dvidServer )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSpeechWithFrom() Ignoring fwd speech message from non-host - from=0x%x", dvidSource );
return FALSE;
}
if ( dwSize < sizeof( DVPROTOCOLMSG_SPEECHWITHFROM ) )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSpeechWithFrom() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
dwSize, dvidSource );
return FALSE;
}
if( !ValidateSpeechPacketSize( m_lpdvfCompressionInfo, dwSize - sizeof( DVPROTOCOLMSG_SPEECHWITHFROM ) ) )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSpeechWithFrom() Ignoring message with invalid speech size, size=0x%x, from=0x%x",
dwSize, dvidSource );
return FALSE;
}
if( !ValidatePlayerDVID( lpdvSpeech->dvidFrom ) )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSpeechWithFrom() Ignoring message with invalid player dvid, flags=0x%x, from=0x%x",
lpdvSpeech->dvidFrom, dvidSource );
return FALSE;
}
DPFX(DPFPREP, DVF_INFOLEVEL, "Received multicast speech!" );
if( lpdvSpeech->dvidFrom == m_dvidLocal )
{
DPFX(DPFPREP, DVF_INFOLEVEL, "Ignoring loopback speech!" );
return TRUE;
}
CVoicePlayer *pPlayerInfo;
hr = m_voiceNameTable.GetEntry( lpdvSpeech->dvidFrom, &pPlayerInfo, TRUE );
if( FAILED( hr ) )
{
DVPROTOCOLMSG_PLAYERJOIN dvPlayerJoin;
dvPlayerJoin.dwFlags = 0;
dvPlayerJoin.dvidID = lpdvSpeech->dvidFrom;
// Call internal create voice player handler -- we've done all validation by now.
InternalCreateVoicePlayer( lpdvSpeech->dvidFrom, &dvPlayerJoin, sizeof( DVPROTOCOLMSG_PLAYERJOIN ) );
}
else
{
ASSERT_VPLAYER(pPlayerInfo);
pPlayerInfo->Release();
}
return QueueSpeech( lpdvSpeech->dvidFrom, &lpdvSpeech->dvHeader, (PBYTE) &lpdvSpeech[1], dwSize-sizeof(DVPROTOCOLMSG_SPEECHWITHFROM) );
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::HandleSpeechBounce"
//
// HandleSpeech
//
// Handles speech data messages
//
BOOL CDirectVoiceClientEngine::HandleSpeechBounce( DVID dvidSource, PDVPROTOCOLMSG_SPEECHHEADER lpdvSpeech, DWORD dwSize )
{
if ( dwSize < sizeof( DVPROTOCOLMSG_SPEECHHEADER ) )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSpeechBounce() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
dwSize, dvidSource );
return FALSE;
}
if( !ValidateSpeechPacketSize( m_lpdvfCompressionInfo, dwSize - sizeof( DVPROTOCOLMSG_SPEECHHEADER ) ) )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSpeechBounce() Ignoring message with invalid speech size, size=0x%x, from=0x%x",
dwSize, dvidSource );
return FALSE;
}
DPFX(DPFPREP, DVF_INFOLEVEL, "Received speech bounce!" );
return QueueSpeech( dvidSource, lpdvSpeech, (PBYTE) &lpdvSpeech[1], dwSize - sizeof( DVPROTOCOLMSG_SPEECHHEADER ) );
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::HandleSpeech"
//
// HandleSpeech
//
// Handles speech data messages
//
BOOL CDirectVoiceClientEngine::HandleSpeech( DVID dvidSource, PDVPROTOCOLMSG_SPEECHHEADER lpdvSpeech, DWORD dwSize )
{
if ( dwSize < sizeof( DVPROTOCOLMSG_SPEECHHEADER ) )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSpeech() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
dwSize, dvidSource );
return FALSE;
}
if( !ValidateSpeechPacketSize( m_lpdvfCompressionInfo, dwSize - sizeof( DVPROTOCOLMSG_SPEECHHEADER ) ) )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSpeech() Ignoring message with invalid speech size, size=0x%x, from=0x%x",
dwSize, dvidSource );
return FALSE;
}
DPFX(DPFPREP, DVF_INFOLEVEL, "Received bare speech!" );
// Ignore speech from ourselves
if( dvidSource == m_dvidLocal )
{
DPFX(DPFPREP, DVF_INFOLEVEL, "Ignoring loopback speech!" );
return TRUE;
}
return QueueSpeech( dvidSource, lpdvSpeech, (PBYTE) &lpdvSpeech[1], dwSize - sizeof( DVPROTOCOLMSG_SPEECHHEADER ) );
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::Cleanup"
//
// Cleanup
//
// WARNING: Do not call this function multiple times on the same object.
//
// This function shuts down the recording and playback threads, shuts down
// the sound system and unhooks the object from the dplay object.
//
// Called By:
// - DoDisconnect
// - Destructor
// - HandleConnectAccept
// - NotifyThread
//
// Locks Required:
// - Global Write Lock
//
void CDirectVoiceClientEngine::Cleanup()
{
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Cleanup called!" );
// Enter cleanup critical section. Only one instance should be in here at a time.
DNEnterCriticalSection( &m_csCleanupProtect );
CDVCSLock guardLock(&m_csClassLock);
guardLock.Lock();
// We only need to cleanup if we're not idle
if( m_dwCurrentState == DVCSTATE_IDLE )
{
DNLeaveCriticalSection( &m_csCleanupProtect );
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Cleanup not required" );
return;
}
if( m_hRecordThreadHandle )
{
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Signalling record to terminate" );
// Signal record thread to shutdown
SetEvent( m_hRecordTerminate );
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Record signalled" );
// Release write lock, if we don't we may have a deadlock
// because recordthread can't get dplay lock, which means
// can't shutdown
//
// Part of a three way deadlock case.
guardLock.Unlock();
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Waiting for record shutdown" );
WaitForSingleObject( m_hRecordDone, INFINITE );
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Record shutdown complete" );
// Re-acquire lock, we need it.
guardLock.Lock();
m_hRecordThreadHandle = NULL;
}
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Record cleanup" );
if( m_hRecordTerminate )
{
CloseHandle( m_hRecordTerminate );
m_hRecordTerminate = NULL;
}
if( m_hRecordDone )
{
CloseHandle( m_hRecordDone );
m_hRecordDone = NULL;
}
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Record cleanup complete" );
if( m_hPlaybackThreadHandle != NULL )
{
// Signal playback thread to shutdown
SetEvent( m_hPlaybackTerminate );
// Release write lock to prevent deadlock.
//
guardLock.Unlock();
WaitForSingleObject( m_hPlaybackDone, INFINITE );
// Re-acquire lock, we need it.
guardLock.Lock();
m_hPlaybackThreadHandle = NULL;
}
if( m_hPlaybackTerminate )
{
CloseHandle( m_hPlaybackTerminate );
m_hPlaybackTerminate = NULL;
}
if( m_hPlaybackDone )
{
CloseHandle( m_hPlaybackDone );
m_hPlaybackDone = NULL;
}
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Playback Thread Done" );
ClientStats_End();
DPFX(DPFPREP, DVF_INFOLEVEL, "Threads gone!" );
// If we're running in client/server we need destroy the server
// buffer.
if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_MIXING ||
m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_ECHO )
{
DeInitializeClientServer();
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Client/Server Gone" );
}
guardLock.Unlock();
// Disable notifications, no notifications after this point can be made
NotifyQueue_Disable();
// The following code is extremely sensitive.
//
// Be careful about the order here, it's important.
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Waiting for outstanding sends" );
// Wait for outstanding buffer sends to complete before
// continuing. Otherwise you have potential crash / leak
// condition
WaitForBufferReturns();
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Disconnecting transport" );
// After this function returns DirectPlay will no longer sends
// us indication, but some may still be in progress.
m_lpSessionTransport->DisableReceiveHook( );
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Flushing notification queue" );
// Ensuring that all notifications have been sent
NotifyQueue_Flush();
// Waiting for transport to return all the threads it is indicating into us on.
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Waiting for transport threads to complete" );
m_lpSessionTransport->WaitForDetachCompletion();
// Re-enable notifications
NotifyQueue_Enable();
guardLock.Lock();
if( m_pTimer != NULL )
{
delete m_pTimer;
m_pTimer = NULL;
}
if( m_thTimerInfo.hPlaybackTimerEvent != NULL )
{
CloseHandle( m_thTimerInfo.hPlaybackTimerEvent );
m_thTimerInfo.hPlaybackTimerEvent = NULL;
}
if( m_thTimerInfo.hRecordTimerEvent != NULL )
{
CloseHandle( m_thTimerInfo.hRecordTimerEvent );
m_thTimerInfo.hRecordTimerEvent = NULL;
}
CleanupPlaybackLists();
CleanupNotifyLists();
// Inform player of their own exit if they were connected!
if( m_fLocalPlayerNotify )
{
DVMSG_DELETEVOICEPLAYER dvMsgDelete;
dvMsgDelete.dvidPlayer = m_dvidLocal;
dvMsgDelete.dwSize = sizeof( DVMSG_DELETEVOICEPLAYER );
dvMsgDelete.pvPlayerContext = m_pvLocalPlayerContext;
m_pvLocalPlayerContext = NULL;
m_fLocalPlayerNotify = FALSE;
m_fLocalPlayerAvailable = FALSE;
TransmitMessage( DVMSGID_DELETEVOICEPLAYER, &dvMsgDelete, sizeof( DVMSG_DELETEVOICEPLAYER ) );
}
m_voiceNameTable.DeInitialize( (m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_PEER), m_lpUserContext, m_lpMessageHandler);
// Hold off on the shutdown of the sound system so user notifications
// of delete players on unravel of nametable can be handled correctly.
//
ShutdownSoundSystem();
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Sound system shutdown" );
m_fpPlayers.DeInitialize();
FreeBuffers();
if( m_pFramePool != NULL )
{
DNEnterCriticalSection( &CDirectVoiceEngine::s_csSTLLock );
delete m_pFramePool;
m_pFramePool = NULL;
DNLeaveCriticalSection( &CDirectVoiceEngine::s_csSTLLock );
}
SetCurrentState( DVCSTATE_IDLE );
DNEnterCriticalSection( &m_csTargetLock );
if( m_pdvidTargets != NULL )
{
delete [] m_pdvidTargets;
m_pdvidTargets = NULL;
m_dwNumTargets = 0;
m_dwTargetVersion = 0;
}
DNLeaveCriticalSection( &m_csTargetLock );
DNLeaveCriticalSection( &m_csCleanupProtect );
ClientStats_Dump();
m_pStatsBlob = NULL;
SetEvent( m_hDisconnectAck );
guardLock.Unlock();
if( m_lpdvServerMigrated != NULL )
{
m_lpdvServerMigrated->Release();
m_lpdvServerMigrated = NULL;
}
}
// WaitForBufferReturns
//
// This function waits until oustanding sends have completed before continuing
// we use this to ensure we don't deregister with outstanding sends.
//
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::WaitForBufferReturns"
void CDirectVoiceClientEngine::WaitForBufferReturns()
{
while( 1 )
{
if( m_BufferDescPool.GetInUseCount() == 0 )
{
break;
}
Sleep( 20 );
}
return;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::DoDisconnect"
//
// DoDisconnect
//
// Performs a disconnection and informs the callback function.
//
// Used for both session lost and normal disconnects.
//
// Called By:
// - NotifyThread
//
void CDirectVoiceClientEngine::DoDisconnect()
{
// Guard to prevent this function from being called more then once on the
// same object
if( m_dwCurrentState == DVCSTATE_IDLE ||
m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
return;
// This codeblock ensures that this path will only be run once per connection. Protects against
// multiple disconnect confirm messages sent to the client.
DNEnterCriticalSection( &m_csCleanupProtect );
if( m_fDisconnecting )
{
DNLeaveCriticalSection( &m_csCleanupProtect );
return;
}
m_fDisconnecting = TRUE;
DNLeaveCriticalSection( &m_csCleanupProtect );
m_dwCurrentState = DVCSTATE_DISCONNECTING;
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "DoDisconnect called!" );
Cleanup();
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Cleanup complete" );
if( m_fSessionLost )
{
DVMSG_SESSIONLOST dvSessionLost;
dvSessionLost.hrResult = m_hrDisconnectResult;
dvSessionLost.dwSize = sizeof( DVMSG_SESSIONLOST );
NotifyQueue_Add( DVMSGID_SESSIONLOST, &dvSessionLost, sizeof( DVMSG_SESSIONLOST ) );
}
else
{
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Signalling disconnect result" );
SendDisconnectResult();
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Done signalling disconnect" );
}
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Disconnect complete" );
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::HandleDisconnect"
// HandleDisconnect
//
// This function is called when a disconnect message is received from the
// server.
//
BOOL CDirectVoiceClientEngine::HandleDisconnectConfirm( DVID dvidSource, PDVPROTOCOLMSG_DISCONNECT lpdvDisconnect, DWORD dwSize )
{
if ( dwSize != sizeof( DVPROTOCOLMSG_DISCONNECT ) )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleDisconnectConfirm() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
dwSize, dvidSource );
return FALSE;
}
// Ignore disconnect message from someone who is not the server
if( dvidSource != m_dvidServer )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleDisconnectConfirm() Ignoring disconnect from non-server" );
return FALSE;
}
// We're not in the disconnecting state, so this message is irrelevant
if( m_dwCurrentState != DVCSTATE_DISCONNECTING )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleDisconnectConfirm() Ignoring disconnect as we're not disconnecting!" );
return FALSE;
}
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "DisconnectConfirm received, signalling worker [res=0x%x]", lpdvDisconnect->hresDisconnect );
DoSignalDisconnect( lpdvDisconnect->hresDisconnect );
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "DisconnectConfirm received, signalled worker" );
return TRUE;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::HandleSessionLost"
BOOL CDirectVoiceClientEngine::HandleSessionLost( DVID dvidSource, PDVPROTOCOLMSG_SESSIONLOST lpdvSessionLost, DWORD dwSize )
{
// This messages can only come from the server.
if( dvidSource != m_dvidServer )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "Ignoring session lost message from non-host - from=0x%x", dvidSource );
return FALSE;
}
if ( dwSize != sizeof( DVPROTOCOLMSG_SESSIONLOST ) )
{
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSessionLost() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
dwSize, dvidSource );
return FALSE;
}
DPFX(DPFPREP, DVF_ENTRYLEVEL, "DVCE::HandleSessionLost() begin" );
DPFX(DPFPREP, DVF_ERRORLEVEL, "<><><><><><><> Session Host has shutdown - Voice Session is gone." );
DoSessionLost( lpdvSessionLost->hresReason );
return TRUE;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::StartTransportSession"
HRESULT CDirectVoiceClientEngine::StartTransportSession( )
{
return S_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::StopTransportSession"
// StopSession
//
// This function is called when the directplay session is lost or stops
// before DirectXVoice is disconnected.
//
HRESULT CDirectVoiceClientEngine::StopTransportSession()
{
DoSessionLost( DVERR_SESSIONLOST );
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::AddPlayer"
HRESULT CDirectVoiceClientEngine::AddPlayer( DVID dvID )
{
return S_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::HandleLocalHostMigrateCreate"
HRESULT CDirectVoiceClientEngine::HandleLocalHostMigrateCreate()
{
LPDIRECTVOICESERVEROBJECT lpdvsServerObject = NULL;
DWORD dwSessionSize = 0;
HRESULT hr = DP_OK;
CDirectVoiceDirectXTransport *pTransport;
// Prevent double-create from host migration run.
CDVCSLock m_guardLock(&m_csClassLock);
m_guardLock.Lock();
if( m_lpdvServerMigrated )
{
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Duplicate host create received. Ignoring" );
return DV_OK;
}
DPFX(DPFPREP, DVF_ERRORLEVEL, "Local client has become the new host. Creating a host" );
hr = DVS_Create( &lpdvsServerObject );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to create server object. hr=0x%x", hr );
goto HOSTCREATE_FAILURE;
}
// Grab a reference local of the new host interface
m_lpdvServerMigrated = (LPDIRECTPLAYVOICESERVER) lpdvsServerObject;
m_guardLock.Unlock();
IncrementObjectCount();
lpdvsServerObject->lIntRefCnt++;
DVMSG_LOCALHOSTSETUP dvMsgLocalHostSetup;
dvMsgLocalHostSetup.dwSize = sizeof( DVMSG_LOCALHOSTSETUP );
dvMsgLocalHostSetup.pvContext = NULL;
dvMsgLocalHostSetup.pMessageHandler = NULL;
TransmitMessage( DVMSGID_LOCALHOSTSETUP, &dvMsgLocalHostSetup, sizeof( DVMSG_LOCALHOSTSETUP ) );
pTransport = (CDirectVoiceDirectXTransport *) m_lpSessionTransport;
hr = DV_Initialize( lpdvsServerObject, pTransport->GetTransportInterface(), dvMsgLocalHostSetup.pMessageHandler, dvMsgLocalHostSetup.pvContext, NULL, 0 );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to initialize the server object hr=0x%x", hr );
goto HOSTCREATE_FAILURE;
}
hr = lpdvsServerObject->lpDVServerEngine->HostMigrateStart( &m_dvSessionDesc, m_dwHostOrderID+DVMIGRATE_ORDERID_OFFSET );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error starting server object hr=0x%x", hr );
goto HOSTCREATE_FAILURE;
}
DVMSG_HOSTMIGRATED dvHostMigrated;
dvHostMigrated.dvidNewHostID = m_lpSessionTransport->GetLocalID();
dvHostMigrated.pdvServerInterface = (LPDIRECTPLAYVOICESERVER) lpdvsServerObject;
dvHostMigrated.dwSize = sizeof( DVMSG_HOSTMIGRATED );
NotifyQueue_Add( DVMSGID_HOSTMIGRATED, &dvHostMigrated, sizeof( DVMSG_HOSTMIGRATED ) );
return DV_OK;
HOSTCREATE_FAILURE:
DPFX(DPFPREP, DVF_ERRORLEVEL, "Informing clients of our failure to create host" );
Send_SessionLost();
if( lpdvsServerObject != NULL )
{
DVS_Release( lpdvsServerObject );
}
return hr;
}
// Handles remove player message
//
// This message triggers handling of host migration if the player
// who has dropped out happens to be the session host.
//
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::RemovePlayer"
HRESULT CDirectVoiceClientEngine::RemovePlayer( DVID dvID )
{
HRESULT hr;
CDVCSLock m_guardLock(&m_csClassLock);
m_guardLock.Lock();
if( m_dwCurrentState == DVCSTATE_DISCONNECTING )
{
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Ignoring transport disconnect for 0x%x -- client is disconnecting", dvID );
return DV_OK;
}
m_guardLock.Unlock();
DVPROTOCOLMSG_PLAYERQUIT dvPlayerQuit;
dvPlayerQuit.dwType = DVMSGID_DELETEVOICEPLAYER;
dvPlayerQuit.dvidID = dvID;
HandleDeleteVoicePlayer( 0, &dvPlayerQuit, sizeof( DVPROTOCOLMSG_PLAYERQUIT ) );
if( dvID == m_lpSessionTransport->GetServerID() )
{
// Don't run host migration code unless it is appropriate
// We could simplify this, but instead using existing code.
DWORD dwTransportSessionType, dwTransportFlags;
hr = m_lpSessionTransport->GetTransportSettings( &dwTransportSessionType, &dwTransportFlags );
if( FAILED( hr ) )
{
DNASSERT( FALSE );
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to retrieve transport settings hr=0x%x", hr );
return DV_OK;
}
if( (m_dvSessionDesc.dwSessionType != DVSESSIONTYPE_PEER ) ||
(m_dvSessionDesc.dwFlags & DVSESSION_NOHOSTMIGRATION) ||
!(dwTransportFlags & DVTRANSPORT_MIGRATEHOST) )
{
DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Host migration is disabled." );
DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Host is gone. Session will exit soon." );
if( m_dwCurrentState == DVCSTATE_CONNECTING )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Aborting connection..." );
SendConnectResult();
SetEvent( m_hConnectAck );
}
DoSessionLost(DVERR_SESSIONLOST);
return DV_OK;
}
}
// The person who dropped out was the server
if( dvID == m_lpSessionTransport->GetServerID() )
{
DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Checking to see if remove of 0x%x is host 0x%x", dvID, m_lpSessionTransport->GetServerID() );
DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Triggered by Remove Player Message" );
MigrateHost_RunElection();
}
return S_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::SetCurrentState"
// SetCurrentState
//
// Sets the current state of the client engine
//
void CDirectVoiceClientEngine::SetCurrentState( DWORD dwState )
{
CDVCSLock m_guardLock(&m_csClassLock);
m_guardLock.Lock();
m_dwCurrentState = dwState;
m_guardLock.Unlock();
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::CheckShouldSendMessage"
//
// CheckShouldSendMessage
//
// Checks the notification mask to see if the specified message type should
// be sent to the user.
//
BOOL CDirectVoiceClientEngine::CheckShouldSendMessage( DWORD dwMessageType )
{
if( m_lpMessageHandler == NULL )
{
return FALSE;
}
BFCSingleLock slLock( &m_csNotifyLock );
slLock.Lock();
BOOL fSend = FALSE;
if( m_dwNumMessageElements == 0 )
{
return TRUE;
}
else
{
for( DWORD dwIndex = 0; dwIndex < m_dwNumMessageElements; dwIndex++ )
{
if( m_lpdwMessageElements[dwIndex] == dwMessageType )
{
return TRUE;
}
}
}
return FALSE;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::TransmitMessage"
//
// TransmitMessage
//
// Called to send a notification to the user.
//
// Only the notify thread should call this function, all other threads should queue up
// notifications by calling NotifyQueue_Add.
//
// Called By:
// - NotifyThread.
//
void CDirectVoiceClientEngine::TransmitMessage( DWORD dwMessageType, LPVOID lpData, DWORD dwSize )
{
if( CheckShouldSendMessage( dwMessageType ) )
{
(*m_lpMessageHandler)( m_lpUserContext, dwMessageType, (!dwSize) ? NULL : lpData );
}
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::CheckForDuplicateObjects"
HRESULT CDirectVoiceClientEngine::CheckForDuplicateObjects()
{
HRESULT hr;
LPKSPROPERTYSET lpksPropSet = NULL;
DSPROPERTY_DIRECTSOUND_OBJECTS_DATA* pDSList = NULL;
DSPROPERTY_DIRECTSOUNDCAPTURE_OBJECTS_DATA* pDSCList = NULL;
DWORD dwIndex;
BOOL fFound;
hr = DirectSoundPrivateCreate( &lpksPropSet );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Unable to check for usage of duplicate devices hr=0x%x", hr );
return DV_OK;
}
hr = PrvGetDirectSoundObjects( lpksPropSet, GUID_NULL, &pDSList );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Unable to retrieve process sound objs hr=0x%x", hr );
hr = DV_OK;
}
// Check list for duplicate of the one we are using.
else
{
// User specified a device, be nice and print a debug message if the object doesn't
// match the GUID
if( m_dvSoundDeviceConfig.lpdsPlaybackDevice != NULL )
{
fFound = FALSE;
// Check the internal list
for( dwIndex = 0; dwIndex < pDSList->Count; dwIndex++ )
{
// Check to see if object user specified matches this one.
if( pDSList->Objects[dwIndex].DirectSound == m_dvSoundDeviceConfig.lpdsPlaybackDevice )
{
if( m_dvSoundDeviceConfig.guidPlaybackDevice != pDSList->Objects[dwIndex].DeviceId )
{
// Expected behaviour with emulated object
DPFX(DPFPREP, DVF_ERRORLEVEL, "Specified GUID is not correct for specified dsound object" );
fFound = FALSE;
}
else
{
fFound = TRUE;
DPFX(DPFPREP, DVF_INFOLEVEL, "GUID for device matches object. User param valid" );
}
break;
}
}
// We didn't find specified object in dsound's list.
// Could be an error condition, but would prevent emulated objects from working.
if( !fFound )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Specified DirectSound object does not exist" );
}
}
// No object specified.
else
{
// Check the internal list to see if we need to make use of an existing object
for( dwIndex = 0; dwIndex < pDSList->Count; dwIndex++ )
{
// We have a winner, a matching playback device.
if( m_dvSoundDeviceConfig.guidPlaybackDevice == pDSList->Objects[dwIndex].DeviceId )
{
hr = pDSList->Objects[dwIndex].DirectSound->QueryInterface( IID_IDirectSound, (void **) &m_dvSoundDeviceConfig.lpdsPlaybackDevice );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to retrieve dsound int for existing dsound object hr=0x%x", hr );
hr = DVERR_INVALIDDEVICE;
goto EXIT_SOUND_CHECK;
}
break;
}
}
}
}
hr = PrvGetDirectSoundCaptureObjects( lpksPropSet, GUID_NULL, &pDSCList );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Unable to retrieve process cap objs hr=0x%x", hr );
hr = DV_OK;
}
// Check list for duplicate of the one we are using
else
{
// User specified a device, be nice and print a debug message if the object doesn't
// match the GUID
if( m_dvSoundDeviceConfig.lpdsCaptureDevice != NULL )
{
fFound = FALSE;
// Check the internal list
for( dwIndex = 0; dwIndex < pDSCList->Count; dwIndex++ )
{
// Check to see if object user specified matches this one.
if( pDSCList->Objects[dwIndex].DirectSoundCapture == m_dvSoundDeviceConfig.lpdsCaptureDevice )
{
if( m_dvSoundDeviceConfig.guidCaptureDevice != pDSCList->Objects[dwIndex].DeviceId )
{
// Expected behaviour with emulated object
DPFX(DPFPREP, DVF_ERRORLEVEL, "Specified GUID is not correct for specified dsound object" );
fFound = FALSE;
}
else
{
fFound = TRUE;
DPFX(DPFPREP, DVF_INFOLEVEL, "GUID for device matches object. User param valid" );
}
break;
}
}
// We didn't find specified object in dsound's list.
// Could be an error condition, but would prevent emulated objects from working.
if( !fFound )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Specified DirectSoundCap object does not exist" );
}
}
}
EXIT_SOUND_CHECK:
if( pDSList != NULL )
{
delete [] pDSList;
}
if( pDSCList != NULL )
{
delete [] pDSCList;
}
lpksPropSet->Release();
return hr;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::InitializeSoundSystem"
// InitializeSoundSystem
//
// Starts up the sound system based on the parameters.
//
HRESULT CDirectVoiceClientEngine::InitializeSoundSystem()
{
HRESULT hr;
Diagnostics_Begin( s_fDumpDiagnostics, "dpv_main.txt" );
DSERRTRACK_Reset();
Diagnostics_DeviceInfo( &m_dvSoundDeviceConfig.guidPlaybackDevice, &m_dvSoundDeviceConfig.guidCaptureDevice );
DPFX(DPFPREP, DVF_ENTRYLEVEL, "DVCE::InitializeSoundSystem() Begin" );
// Note: Mapping of default devices has already been performed.
// On Pre DX7.1 systems, all default devices map to GUID_NULL
// On DX7.1 and later, default device will have been mapped to their real GUIDs
//
m_dwCompressedFrameSize = m_lpdvfCompressionInfo->dwFrameLength;
m_dwUnCompressedFrameSize = DVCDB_CalcUnCompressedFrameSize( m_lpdvfCompressionInfo, s_lpwfxPlaybackFormat );
m_dwNumPerBuffer = m_lpdvfCompressionInfo->dwFramesPerBuffer;
// Setup the description for the main playback buffer
//
// Needs to be after above because it depends on proper values above
SetupPlaybackBufferDesc( &m_dsBufferDesc, NULL );
// If they gave us an object, just use it
if( m_dvSoundDeviceConfig.lpdsPlaybackDevice != NULL )
{
CDirectSoundPlaybackDevice *tmpDevice;
tmpDevice = new CDirectSoundPlaybackDevice( );
if( tmpDevice == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to alloc memory" );
return DVERR_OUTOFMEMORY;
}
hr = tmpDevice->Initialize( m_dvSoundDeviceConfig.lpdsPlaybackDevice, m_dvSoundDeviceConfig.guidPlaybackDevice );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error initalizing playback device from specified object hr=0x%x", hr );
delete tmpDevice;
return hr;
}
m_audioPlaybackDevice = tmpDevice;
}
// If they gave us an object, just use it
if( m_dvSoundDeviceConfig.lpdsCaptureDevice != NULL )
{
CDirectSoundCaptureRecordDevice *tmpRecDevice;
tmpRecDevice = new CDirectSoundCaptureRecordDevice( );
if( tmpRecDevice == NULL )
{
if( m_audioPlaybackDevice != NULL )
{
delete m_audioPlaybackDevice;
m_audioPlaybackDevice = NULL;
}
Diagnostics_Write( DVF_ERRORLEVEL, "Failed to alloc memory" );
return DVERR_OUTOFMEMORY;
}
hr = tmpRecDevice->Initialize( m_dvSoundDeviceConfig.lpdsCaptureDevice, m_dvSoundDeviceConfig.guidCaptureDevice );
if( FAILED( hr ) )
{
Diagnostics_Write( DVF_ERRORLEVEL, "Error initializing record device from specified object hr=0x%x", hr );
if( m_audioPlaybackDevice != NULL )
{
delete m_audioPlaybackDevice;
m_audioPlaybackDevice = NULL;
}
delete tmpRecDevice;
return hr;
}
m_audioRecordDevice = tmpRecDevice;
}
// We were passed a buffer by the user
if( m_dvSoundDeviceConfig.lpdsMainBuffer )
{
m_audioPlaybackBuffer = new CDirectSoundPlaybackBuffer( m_dvSoundDeviceConfig.lpdsMainBuffer );
if( !m_audioPlaybackBuffer )
{
Diagnostics_Write( DVF_ERRORLEVEL, "Error allocating memory" );
if( m_audioPlaybackDevice )
{
delete m_audioPlaybackDevice;
m_audioPlaybackDevice = NULL;
}
if( m_audioRecordDevice )
{
delete m_audioRecordDevice;
m_audioRecordDevice = NULL;
}
return DVERR_OUTOFMEMORY;
}
DPFX(DPFPREP, DVF_INFOLEVEL, "Creating a buffer using buffer user gave us." );
}
// If we haven't initialized half duplex
if( !(m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_HALFDUPLEX) )
{
hr = InitFullDuplex( m_dvSoundDeviceConfig.hwndAppWindow,
m_dvSoundDeviceConfig.guidPlaybackDevice,
&m_audioPlaybackDevice,
&m_dsBufferDesc,
&m_audioPlaybackBuffer,
m_dvSoundDeviceConfig.guidCaptureDevice,
&m_audioRecordDevice,
&m_audioRecordBuffer,
m_lpdvfCompressionInfo->guidType,
this->s_lpwfxPrimaryFormat,
this->s_lpwfxPlaybackFormat,
this->s_fASO,
this->m_dvSoundDeviceConfig.dwMainBufferPriority,
this->m_dvSoundDeviceConfig.dwMainBufferFlags,
m_dvSoundDeviceConfig.dwFlags );
// Full duplex init failed, set the half duplex flag
if( FAILED( hr ) )
{
// Records are deleted here because not needed for half-duplex
if( m_audioRecordDevice != NULL )
{
delete m_audioRecordDevice;
m_audioRecordDevice = NULL;
}
if( m_audioRecordBuffer != NULL )
{
delete m_audioRecordBuffer;
m_audioRecordBuffer = NULL;
}
// If we got playbacks from user then let them fall through to half-duplex
// If we allocated them in InitFullDuplex - then it cleans up for itself on error
m_dvSoundDeviceConfig.dwFlags |= DVSOUNDCONFIG_HALFDUPLEX;
}
// Full duplex passed
else
{
}
if( hr == E_OUTOFMEMORY )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Full duplex init received an E_OUTOFMEMORY, failing Initialization()." );
return DVERR_OUTOFMEMORY;
}
}
if( m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_HALFDUPLEX )
{
hr = InitHalfDuplex( m_dvSoundDeviceConfig.hwndAppWindow,
m_dvSoundDeviceConfig.guidPlaybackDevice,
&m_audioPlaybackDevice,
&m_dsBufferDesc,
&m_audioPlaybackBuffer,
m_lpdvfCompressionInfo->guidType,
this->s_lpwfxPrimaryFormat,
this->s_lpwfxPlaybackFormat,
this->m_dvSoundDeviceConfig.dwMainBufferPriority,
this->m_dvSoundDeviceConfig.dwMainBufferFlags,
m_dvSoundDeviceConfig.dwFlags );
if( FAILED( hr ) )
{
// if user supplied these then we need to nuke them here
// if user didn't supply, then they were nuked on error in InitHalfDuplex
if( m_audioPlaybackDevice != NULL )
{
delete m_audioPlaybackDevice;
m_audioPlaybackDevice = NULL;
}
if( m_audioPlaybackBuffer != NULL )
{
delete m_audioPlaybackBuffer;
m_audioPlaybackBuffer = NULL;
}
return hr;
}
}
// Build the frame pool
DNEnterCriticalSection( &CDirectVoiceEngine::s_csSTLLock );
m_pFramePool = new CFramePool( m_dwCompressedFrameSize );
DNLeaveCriticalSection( &CDirectVoiceEngine::s_csSTLLock );
m_lpstBufferList = NULL;
if( m_pFramePool == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to allocate frame pool" );
return DVERR_OUTOFMEMORY;
}
if (!m_pFramePool->Init())
{
delete m_pFramePool;
return DVERR_OUTOFMEMORY;
}
if( m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_SETCONVERSIONQUALITY )
{
if( m_dvSoundDeviceConfig.guidPlaybackDevice != GUID_NULL )
{
hr = m_audioPlaybackDevice->GetMixerQuality( &m_dwOriginalPlayQuality );
if( FAILED( hr ) )
{
DPFX(DPFPREP, 1, "Unable to get current playback quality hr=0x%x", hr );
m_dwOriginalPlayQuality = DV_CLIENT_SRCQUALITY_INVALID;
}
// We're already at the setting, someone else probably runinng, disable restore
else if( m_dwOriginalPlayQuality == DIRECTSOUNDMIXER_SRCQUALITY_ADVANCED )
{
DPFX(DPFPREP, 1, "Quality setting is already at correct value. Will not set/restore" );
m_dwOriginalPlayQuality = DV_CLIENT_SRCQUALITY_INVALID;
}
else
{
hr = m_audioPlaybackDevice->SetMixerQuality( DIRECTSOUNDMIXER_SRCQUALITY_ADVANCED );
}
}
else
{
hr = DVERR_NOTSUPPORTED;
}
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Unable to set mixer quality for playback device hr=0x%x", hr );
}
}
else
{
m_dwOriginalPlayQuality = DV_CLIENT_SRCQUALITY_INVALID;
}
// Add a reference for the soundconfig struct if one doesn't exist
if( m_dvSoundDeviceConfig.lpdsPlaybackDevice == NULL )
{
m_dvSoundDeviceConfig.lpdsPlaybackDevice = m_audioPlaybackDevice->GetPlaybackDevice();
m_dvSoundDeviceConfig.lpdsPlaybackDevice->AddRef();
}
// If we're not half duplex, do some initial sets for the recording system
if( !(m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_HALFDUPLEX) &&
m_audioRecordDevice != NULL )
{
if( m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_SETCONVERSIONQUALITY )
{
DPFX(DPFPREP, DVF_INFOLEVEL, "Setting quality" );
DV_DUMP_GUID( m_dvSoundDeviceConfig.guidCaptureDevice );
if( m_dvSoundDeviceConfig.lpdsCaptureDevice == NULL )
{
m_dvSoundDeviceConfig.lpdsCaptureDevice = m_audioRecordDevice->GetCaptureDevice();
m_dvSoundDeviceConfig.lpdsCaptureDevice->AddRef();
}
if( m_dvSoundDeviceConfig.guidCaptureDevice != GUID_NULL )
{
DPFX(DPFPREP, DVF_INFOLEVEL, "Setting quality 2" );
hr = m_audioRecordDevice->GetMixerQuality( &m_dwOriginalRecordQuality );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Unable to get conversion quality hr=0x%x", hr );
m_dwOriginalRecordQuality = DV_CLIENT_SRCQUALITY_INVALID;
}
// We're already at the setting, someone else probably runinng, disable restore
else if( m_dwOriginalRecordQuality == DIRECTSOUNDMIXER_SRCQUALITY_ADVANCED )
{
DPFX(DPFPREP, 1, "Play Quality setting is already at correct value. Will not set/restore" );
m_dwOriginalRecordQuality = DV_CLIENT_SRCQUALITY_INVALID;
}
else
{
hr = m_audioRecordDevice->SetMixerQuality( DIRECTSOUNDMIXER_SRCQUALITY_ADVANCED );
}
}
else
{
hr = DVERR_NOTSUPPORTED;
}
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Setting failed" );
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Unable to set mixer quality for record device hr=0x%x", hr );
}
}
else
{
m_dwOriginalRecordQuality = DV_CLIENT_SRCQUALITY_INVALID;
}
// If we haven't set no focus
if( !(m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_NOFOCUS ) )
{
// Recording is started muted
if( m_dvClientConfig.dwFlags & DVCLIENTCONFIG_RECORDMUTE )
{
DPFX(DPFPREP, DVF_INFOLEVEL, "Record Muted: Yielding focus" );
hr = m_audioRecordBuffer->YieldFocus();
}
else
{
DPFX(DPFPREP, DVF_INFOLEVEL, "Record Un-Muted: Attempting to reclaim focus" );
hr = m_audioRecordBuffer->ClaimFocus();
}
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Focus set failed hr=0x%x", hr );
}
}
LONG lVolume;
hr = m_audioRecordBuffer->GetVolume( &lVolume );
if( FAILED( hr ) )
{
Diagnostics_Write( 0, "Unable to retrieve recording volume hr=0x%x", hr );
Diagnostics_Write(0, "Disabling recording controls" );
m_dvSoundDeviceConfig.dwFlags |= DVSOUNDCONFIG_NORECVOLAVAILABLE;
}
}
else
{
m_dvSoundDeviceConfig.lpdsCaptureDevice = NULL;
}
// Initialize the sound target list
InitSoundTargetList();
hr = CreateGeneralBuffer();
if( FAILED( hr ) )
{
Diagnostics_Write( 0, "Error creating general buffer hr=0x%x", hr );
return hr;
}
DPFX(DPFPREP, DVF_ENTRYLEVEL, "DVCE::InitializeSoundSystem() End" );
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::ShutdownSoundSystem"
// ShutdownSoundSystem
//
// Stop the sound system
//
HRESULT CDirectVoiceClientEngine::ShutdownSoundSystem()
{
DPFX(DPFPREP, DVF_ENTRYLEVEL, "DVCE::ShutdownSoundSystem() Begin" );
HRESULT hr;
FreeSoundTargetList();
#ifndef __DISABLE_SOUND
if( m_audioRecordBuffer != NULL )
{
delete m_audioRecordBuffer;
m_audioRecordBuffer = NULL;
}
if( m_dvSoundDeviceConfig.lpdsMainBuffer != NULL )
{
m_dvSoundDeviceConfig.lpdsMainBuffer->Release();
m_dvSoundDeviceConfig.lpdsMainBuffer = NULL;
}
if( m_audioRecordDevice != NULL )
{
if( m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_SETCONVERSIONQUALITY )
{
if( m_dwOriginalRecordQuality != DV_CLIENT_SRCQUALITY_INVALID )
{
hr = m_audioRecordDevice->SetMixerQuality( m_dwOriginalRecordQuality );
if( FAILED( hr ) )
{
DPFX(DPFPREP, 1, "Failed to restore original recording quality hr=0x%x", hr );
}
}
m_dwOriginalRecordQuality = DV_CLIENT_SRCQUALITY_INVALID;
}
delete m_audioRecordDevice;
m_audioRecordDevice = NULL;
}
if( m_audioPlaybackBuffer != NULL )
{
delete m_audioPlaybackBuffer;
m_audioPlaybackBuffer = NULL;
}
if( m_audioPlaybackDevice != NULL )
{
if( m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_SETCONVERSIONQUALITY )
{
if( m_dwOriginalPlayQuality != DV_CLIENT_SRCQUALITY_INVALID )
{
hr = m_audioPlaybackDevice->SetMixerQuality( m_dwOriginalPlayQuality );
if( FAILED( hr ) )
{
DPFX(DPFPREP, 1, "Failed to restore original playback quality hr=0x%x", hr );
}
}
m_dwOriginalPlayQuality = DV_CLIENT_SRCQUALITY_INVALID;
}
delete m_audioPlaybackDevice;
m_audioPlaybackDevice = NULL;
}
#endif
if( m_dvSoundDeviceConfig.lpdsCaptureDevice != NULL )
{
m_dvSoundDeviceConfig.lpdsCaptureDevice->Release();
m_dvSoundDeviceConfig.lpdsCaptureDevice = NULL;
}
if( m_dvSoundDeviceConfig.lpdsPlaybackDevice != NULL )
{
m_dvSoundDeviceConfig.lpdsPlaybackDevice->Release();
m_dvSoundDeviceConfig.lpdsPlaybackDevice = NULL;
}
DPFX(DPFPREP, DVF_ENTRYLEVEL, "DVCE::ShutdownSoundSystem() End" );
Diagnostics_End();
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::SetConnectResult"
// SetConnectResult
//
// This function stores the specified connect result code in m_hrOriginalConnectResult
// and stores the "translated" error code in m_hrConnectResult. m_hrConnectResult
// is used to return the result to the user.
//
// For some error codes the voice layer translates the code to a voice specific error,
// this is why this function is required.
//
void CDirectVoiceClientEngine::SetConnectResult( HRESULT hrConnectResult )
{
DPFX( DPFPREP, DVF_CONNECT_PROCEDURE_DEBUG_LEVEL, "CONNECT RESULT: Transition [0x%x] to [0x%x]", m_hrOriginalConnectResult, hrConnectResult );
m_hrOriginalConnectResult = hrConnectResult;
if( HRESULT_FACILITY( hrConnectResult ) == static_cast<HRESULT>(_FACDS) )
{
if( hrConnectResult == DSERR_ALLOCATED )
{
DPFX( DPFPREP, DVF_WARNINGLEVEL, "WARNING: Mapping DSERR_ALLOCATED --> DVERR_PLAYBACKSYSTEMERROR" );
m_hrConnectResult = DVERR_PLAYBACKSYSTEMERROR;
}
else if( hrConnectResult == DSERR_OUTOFMEMORY )
{
DPFX( DPFPREP, DVF_WARNINGLEVEL, "WARNING: Mapping DSERR_OUTOFMEMORY --> DVERR_OUTOFMEMORY" );
m_hrConnectResult = DVERR_OUTOFMEMORY;
}
else if( hrConnectResult == DSERR_NODRIVER )
{
DPFX( DPFPREP, DVF_WARNINGLEVEL, "WARNING: Mapping DSERR_NODRIVER --> DVERR_INVALIDDEVICE" );
m_hrConnectResult = DVERR_INVALIDDEVICE;
}
else
{
DPFX( DPFPREP, DVF_WARNINGLEVEL, "WARNING: Mapping 0x%x --> DVERR_SOUNDINITFAILURE", hrConnectResult );
m_hrConnectResult = DVERR_SOUNDINITFAILURE;
}
}
else
{
m_hrConnectResult = hrConnectResult;
}
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::GetConnectResult"
// GetConnectResult
//
// This function stores the specified connect result code in m_hrOriginalConnectResult
// and stores the "translated" error code in m_hrConnectResult. m_hrConnectResult
// is used to return the result to the user.
//
// For some error codes the voice layer translates the code to a voice specific error,
// this is why this function is required.
//
HRESULT CDirectVoiceClientEngine::GetConnectResult( ) const
{
return m_hrConnectResult;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::SendConnectResult"
HRESULT CDirectVoiceClientEngine::SendConnectResult()
{
if( m_fConnectAsync )
{
DVMSG_CONNECTRESULT dvConnect;
dvConnect.hrResult = GetConnectResult();
dvConnect.dwSize = sizeof( DVMSG_CONNECTRESULT );
return NotifyQueue_Add( DVMSGID_CONNECTRESULT, &dvConnect, sizeof( DVMSG_CONNECTRESULT ) );
}
else
{
return DV_OK;
}
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::SendDisconnectResult"
HRESULT CDirectVoiceClientEngine::SendDisconnectResult()
{
if( m_fDisconnectAsync )
{
DVMSG_DISCONNECTRESULT dvDisconnect;
dvDisconnect.hrResult = m_hrDisconnectResult;
dvDisconnect.dwSize = sizeof( DVMSG_DISCONNECTRESULT );
return NotifyQueue_Add( DVMSGID_DISCONNECTRESULT, &dvDisconnect, sizeof( DVMSG_DISCONNECTRESULT ) );
}
else
{
return DV_OK;
}
}
// NotifyThread
//
// All-purpose watch/notification thread.
//
// Wakes up to:
// - Check for multicast player timeout
// - Adjust parameters on notification
// - Sends notification messages to users.
// - Checks for timeouts on connect and disconnect calls
// - Sends level notifications
//
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::NotifyThread"
void CDirectVoiceClientEngine::NotifyThread( void *lpParam )
{
CDirectVoiceClientEngine *This = (CDirectVoiceClientEngine *) lpParam;
HANDLE eventArray[5];
DWORD dwWaitPeriod;
LONG lWaitResult;
DWORD dwPowerLevel;
DWORD dwLastLevelNotify,
dwLastTimeoutCheck,
dwCurTime;
DVMSG_INPUTLEVEL dvInputLevel;
DVMSG_OUTPUTLEVEL dvOutputLevel;
dvInputLevel.dwSize = sizeof( DVMSG_INPUTLEVEL );
dvOutputLevel.dwSize = sizeof( DVMSG_OUTPUTLEVEL );
HRESULT hr;
DVID dvidMessageTarget;
hr = COM_CoInitialize(NULL);
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error initializing COM" );
SetEvent( This->m_hNotifyDone );
This->HandleThreadError( DVERR_GENERIC );
return;
}
eventArray[0] = This->m_hNotifyTerminate;
eventArray[1] = This->m_hNotifyChange;
eventArray[2] = This->m_hNotifyDisconnect;
eventArray[3] = This->m_hNewNotifyElement;
eventArray[4] = This->m_hNotifyConnect;
// Setup last times we checked to right now.
dwLastLevelNotify = GetTickCount();
dwLastTimeoutCheck = dwLastLevelNotify;
dwWaitPeriod = DV_CLIENT_NOTIFYWAKEUP_TIMEOUT;
while( 1 )
{
lWaitResult = WaitForMultipleObjects( 5, eventArray, FALSE, dwWaitPeriod );
DPFX(DPFPREP, DVF_INFOLEVEL, "Wakeing up!" );
// If we were woken up for a reason, handle the reason first
if( lWaitResult != WAIT_TIMEOUT )
{
lWaitResult -= WAIT_OBJECT_0;
if( lWaitResult == 0 )
{
DPFX(DPFPREP, DVF_INFOLEVEL, "Shutting down" );
break;
}
else if( lWaitResult == 1 )
{
DPFX(DPFPREP, DVF_INFOLEVEL, "Parameter Change" );
}
else if( lWaitResult == 2 )
{
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Processing disconnect request" );
This->DoDisconnect();
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Finished processing disconnect" );
continue;
}
else if( lWaitResult == 3 )
{
hr = This->NotifyQueue_IndicateNext();
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "NotifyQueue_Get Failed hr=0x%x", hr );
}
}
else if( lWaitResult == 4 )
{
if( FAILED( This->GetConnectResult() ) )
{
This->Cleanup();
This->SendConnectResult();
SetEvent( This->m_hConnectAck );
}
else
{
This->DoConnectResponse();
}
continue;
}
}
if( This->m_dwCurrentState == DVCSTATE_IDLE )
{
continue;
}
// If we're connecting, check for timeout on the connection
// request.
if( This->m_dwCurrentState == DVCSTATE_CONNECTING && This->m_dwSynchBegin != 0 )
{
dwCurTime = GetTickCount();
if( ( dwCurTime - This->m_dwSynchBegin ) > DV_CLIENT_CONNECT_TIMEOUT )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Connection Timed-Out. Returning NOVOICESESSION" );
This->SetConnectResult( DVERR_NOVOICESESSION );
This->Cleanup();
This->SendConnectResult();
SetEvent( This->m_hConnectAck );
continue;
}
else if( ( dwCurTime - This->m_dwLastConnectSent ) > DV_CLIENT_CONNECT_RETRY_TIMEOUT )
{
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Connect Request Timed-Out" );
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Re-sending connection request" );
hr = This->Send_ConnectRequest();
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error sending connection request. Send error hr=0x%x", hr );
This->SetConnectResult( DVERR_SENDERROR );
This->Cleanup();
This->SendConnectResult();
SetEvent( This->m_hConnectAck );
continue;
}
This->m_dwLastConnectSent = dwCurTime;
}
}
// If we're disconnecting, check for timeout on the disconnect
else if( This->m_dwCurrentState == DVCSTATE_DISCONNECTING )
{
dwCurTime = GetTickCount();
DPFX(DPFPREP, DVF_INFOLEVEL, "Checking timeout on disconnect. Waited %d so far", dwCurTime - This->m_dwSynchBegin );
if( ( dwCurTime - This->m_dwSynchBegin ) > DV_CLIENT_DISCONNECT_TIMEOUT )
{
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Disconnect Request Timed-Out" );
This->DoSignalDisconnect( DVERR_TIMEOUT );
continue;
}
}
// Take care of the periodic checks
if (This->m_dwCurrentState == DVCSTATE_CONNECTED)
{
dwCurTime = GetTickCount();
// Update pending / other lists
This->UpdateActiveNotifyPendingList( );
// If we're running a multicast session.. check for timed-out users
if( This->m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_FORWARDING )
{
if( ( dwCurTime - dwLastTimeoutCheck ) > DV_MULTICAST_USERTIMEOUT_PERIOD )
{
This->CheckForUserTimeout(dwCurTime);
dwLastTimeoutCheck = dwCurTime;
}
}
// Check to see if it's time to notify about levels
if( This->m_dvClientConfig.dwNotifyPeriod != 0 && This->m_fLocalPlayerAvailable )
{
if( ( dwCurTime - dwLastLevelNotify ) > This->m_dvClientConfig.dwNotifyPeriod )
{
dvInputLevel.pvLocalPlayerContext = This->m_pvLocalPlayerContext;
dvOutputLevel.pvLocalPlayerContext = This->m_pvLocalPlayerContext;
if( !(This->m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_HALFDUPLEX) )
{
dvInputLevel.dwPeakLevel = This->m_bLastPeak;
if( This->m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_NORECVOLAVAILABLE )
{
dvInputLevel.lRecordVolume = DSBVOLUME_MAX;
}
else
{
dvInputLevel.lRecordVolume = This->m_dvClientConfig.lRecordVolume;
}
This->NotifyQueue_Add( DVMSGID_INPUTLEVEL, &dvInputLevel, sizeof( DVMSG_INPUTLEVEL ) );
}
dvOutputLevel.dwPeakLevel = This->m_bLastPlaybackPeak;
dvOutputLevel.lOutputVolume = This->m_dvClientConfig.lPlaybackVolume;
This->NotifyQueue_Add( DVMSGID_OUTPUTLEVEL, &dvOutputLevel, sizeof( DVMSG_OUTPUTLEVEL ) );
This->SendPlayerLevels();
dwLastLevelNotify = dwCurTime;
}
}
}
}
// Flush out any remaining notifications
This->NotifyQueue_Flush();
SetEvent( This->m_hNotifyDone );
COM_CoUninitialize();
_endthread();
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::SendPlayerLevels"
//
// SendPlayerLevels
//
// Send player level notifications, but only if there is a message handler
// and the player level message is active.
//
void CDirectVoiceClientEngine::SendPlayerLevels()
{
if( CheckShouldSendMessage( DVMSGID_PLAYEROUTPUTLEVEL ) )
{
DPFX(DPFPREP, DVF_INFOLEVEL, "SendPlayerLevels: Got Lock" );
CVoicePlayer *pCurrentPlayer;
CBilink *pblSearch;
DVMSG_PLAYEROUTPUTLEVEL dvPlayerLevel;
pblSearch = m_blNotifyActivePlayers.GetNext();
while( pblSearch != &m_blNotifyActivePlayers )
{
pCurrentPlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blNotifyList );
ASSERT_VPLAYER(pCurrentPlayer);
if( pCurrentPlayer == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Retrieved NULL player from active list" );
DNASSERT( FALSE );
return;
}
if( pCurrentPlayer->IsReceiving() )
{
dvPlayerLevel.dwSize = sizeof( DVMSG_PLAYEROUTPUTLEVEL );
dvPlayerLevel.dvidSourcePlayerID = pCurrentPlayer->GetPlayerID();
dvPlayerLevel.dwPeakLevel = pCurrentPlayer->GetLastPeak();
dvPlayerLevel.pvPlayerContext = pCurrentPlayer->GetContext();
NotifyQueue_Add( DVMSGID_PLAYEROUTPUTLEVEL, &dvPlayerLevel, sizeof( DVMSG_PLAYEROUTPUTLEVEL ) );
}
pblSearch = pblSearch->GetNext();
}
}
DPFX(DPFPREP, DVF_INFOLEVEL, "SendPlayerLevels: Done Enum" );
return;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::CheckForUserTimeout"
//
// CheckForUserTimeout
//
// Run the list of users and check for user timeouts in multicast sessions
//
void CDirectVoiceClientEngine::CheckForUserTimeout( DWORD dwCurTime )
{
DPFX(DPFPREP, DVF_INFOLEVEL, "Got Lock" );
CBilink *pblSearch;
CVoicePlayer *pCurrentPlayer;
DVPROTOCOLMSG_PLAYERQUIT msgPlayerQuit;
pblSearch = m_blNotifyActivePlayers.GetNext();
msgPlayerQuit.dwType = DVMSGID_DELETEVOICEPLAYER;
while( pblSearch != &m_blNotifyActivePlayers )
{
pCurrentPlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blNotifyList );
ASSERT_VPLAYER(pCurrentPlayer);
if( dwCurTime - pCurrentPlayer->GetLastPlayback() > DV_MULTICAST_USERTIMEOUT_PERIOD )
{
msgPlayerQuit.dvidID = pCurrentPlayer->GetPlayerID();
HandleDeleteVoicePlayer( pCurrentPlayer->GetPlayerID(), &msgPlayerQuit, sizeof( DVPROTOCOLMSG_PLAYERQUIT ) );
}
pblSearch = pblSearch->GetNext();
}
DPFX(DPFPREP, DVF_INFOLEVEL, "Done Enum" );
return;
}
// Cleanup any outstanding entries on the playback lists
void CDirectVoiceClientEngine::CleanupPlaybackLists( )
{
CBilink *pblSearch;
CVoicePlayer *pVoicePlayer;
DNEnterCriticalSection( &m_csPlayAddList );
m_dwPlayActiveCount = 0;
pblSearch = m_blPlayActivePlayers.GetNext();
while( pblSearch != &m_blPlayActivePlayers )
{
pVoicePlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blPlayList );
ASSERT_VPLAYER(pVoicePlayer);
pVoicePlayer->RemoveFromPlayList();
pVoicePlayer->Release();
pblSearch = m_blPlayActivePlayers.GetNext();
}
pblSearch = m_blPlayAddPlayers.GetNext();
while( pblSearch != &m_blPlayAddPlayers )
{
pVoicePlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blPlayList );
ASSERT_VPLAYER(pVoicePlayer);
pVoicePlayer->RemoveFromPlayList();
pVoicePlayer->Release();
pblSearch = m_blPlayAddPlayers.GetNext();
}
DNLeaveCriticalSection( &m_csPlayAddList );
}
// Cleanup any outstanding entries on the playback lists
void CDirectVoiceClientEngine::CleanupNotifyLists( )
{
CBilink *pblSearch;
CVoicePlayer *pVoicePlayer;
DNEnterCriticalSection( &m_csNotifyAddList );
pblSearch = m_blNotifyActivePlayers.GetNext();
while( pblSearch != &m_blNotifyActivePlayers )
{
pVoicePlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blNotifyList );
ASSERT_VPLAYER(pVoicePlayer);
pVoicePlayer->RemoveFromNotifyList();
pVoicePlayer->Release();
pblSearch = m_blNotifyActivePlayers.GetNext();
}
pblSearch = m_blNotifyAddPlayers.GetNext();
while( pblSearch != &m_blNotifyAddPlayers )
{
pVoicePlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blNotifyList );
ASSERT_VPLAYER(pVoicePlayer);
pVoicePlayer->RemoveFromNotifyList();
pVoicePlayer->Release();
pblSearch = m_blNotifyAddPlayers.GetNext();
}
DNLeaveCriticalSection( &m_csNotifyAddList );
}
// UpdateActivePendingList
//
// This function looks at the pending list and moves those elements on the pending list to the active list
//
// This function also looks at the active list and removes those players who are disconnected
//
// There are three four lists in the system:
// - Playback Thread
// - Notify Thread
// - Host Migration List
//
void CDirectVoiceClientEngine::UpdateActivePlayPendingList( )
{
CBilink *pblSearch;
CVoicePlayer *pVoicePlayer;
DNEnterCriticalSection( &m_csPlayAddList );
// Add players who are pending
pblSearch = m_blPlayAddPlayers.GetNext();
while( pblSearch != &m_blPlayAddPlayers )
{
pVoicePlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blPlayList );
ASSERT_VPLAYER(pVoicePlayer);
pVoicePlayer->RemoveFromPlayList();
pVoicePlayer->AddToPlayList( &m_blPlayActivePlayers );
m_dwPlayActiveCount++;
pblSearch = m_blPlayAddPlayers.GetNext();
}
DNLeaveCriticalSection( &m_csPlayAddList );
// Remove players who have disconnected
pblSearch = m_blPlayActivePlayers.GetNext();
while( pblSearch != &m_blPlayActivePlayers )
{
pVoicePlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blPlayList );
ASSERT_VPLAYER(pVoicePlayer);
pblSearch = pblSearch->GetNext();
// If current player has disconnected, remove them from active list
// and release the reference the list has
if( pVoicePlayer->IsDisconnected() )
{
m_dwPlayActiveCount--;
pVoicePlayer->RemoveFromPlayList();
pVoicePlayer->Release();
}
}
}
void CDirectVoiceClientEngine::UpdateActiveNotifyPendingList( )
{
CBilink *pblSearch;
CVoicePlayer *pVoicePlayer;
DNEnterCriticalSection( &m_csNotifyAddList );
// Add players who are pending
pblSearch = m_blNotifyAddPlayers.GetNext();
while( pblSearch != &m_blNotifyAddPlayers )
{
pVoicePlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blNotifyList );
ASSERT_VPLAYER(pVoicePlayer);
pVoicePlayer->RemoveFromNotifyList();
pVoicePlayer->AddToNotifyList( &m_blNotifyActivePlayers );
pblSearch = m_blNotifyAddPlayers.GetNext();
}
DNLeaveCriticalSection( &m_csNotifyAddList );
// Remove players who have disconnected
pblSearch = m_blNotifyActivePlayers.GetNext();
while( pblSearch != &m_blNotifyActivePlayers )
{
pVoicePlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blNotifyList );
ASSERT_VPLAYER(pVoicePlayer);
pblSearch = pblSearch->GetNext();
// If current player has disconnected, remove them from active list
// and release the reference the list has
if( pVoicePlayer->IsDisconnected() )
{
pVoicePlayer->RemoveFromNotifyList();
pVoicePlayer->Release();
}
}
}
HRESULT CDirectVoiceClientEngine::CreateGeneralBuffer( )
{
HRESULT hr;
m_lpstGeneralBuffer = new CSoundTarget( DVID_REMAINING,
m_audioPlaybackDevice,
(CAudioPlaybackBuffer *) m_audioPlaybackBuffer,
&m_dsBufferDesc,
m_dvSoundDeviceConfig.dwMainBufferPriority,
m_dvSoundDeviceConfig.dwMainBufferFlags,
m_dwUnCompressedFrameSize );
if( m_lpstGeneralBuffer == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to allocate General Buffer" );
return DVERR_OUTOFMEMORY;
}
hr = m_lpstGeneralBuffer->GetInitResult();
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to initalize general buffer hr=0x%x", hr );
return hr;
}
if( m_lpstGeneralBuffer->Get3DBuffer() != NULL )
{
// Turn off 3D by default on the general buffer
m_lpstGeneralBuffer->Get3DBuffer()->SetMode( DS3DMODE_DISABLE, DS3D_IMMEDIATE );
}
// Set General Buffer Volume
if( m_lpstGeneralBuffer->GetBuffer() != NULL )
{
m_lpstGeneralBuffer->GetBuffer()->SetVolume( m_dvClientConfig.lPlaybackVolume );
}
hr = m_lpstGeneralBuffer->StartMix();
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to start the mix hr=0x%x.", hr );
return hr;
}
return hr;
}
// New Playback Thread
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::PlaybackThread"
void CDirectVoiceClientEngine::PlaybackThread( void *lParam )
{
DWORD dwCurPlayer;
BOOL bContinueEnum;
DVPROTOCOLMSG_PLAYERQUIT dvPlayerQuit;
DWORD dwResultSize;
LPBYTE lpSourceBuffer = NULL;
HANDLE hEventArray[2] = { NULL, NULL };
LONG lWaitResult;
BOOL fMixed;
CSoundTarget *lpstCurrent = NULL;
BYTE bHighPeak;
HRESULT hr;
DWORD dwEchoState;
DWORD dwTmp;
BYTE bTmpPeak1, bTmpPeak2;
DVMSG_PLAYERVOICESTART dvMsgPlayerVoiceStart;
DVMSG_PLAYERVOICESTOP dvMsgPlayerVoiceStop;
DWORD dwCompressStart;
DWORD dwCompressTime;
CVoicePlayer *pCurrentPlayer;
CBilink *pblSearch;
BOOL fSilence, fLost;
DWORD dwCurrentTime;
DWORD dwSeqNum, dwMsgNum;
CFrame *frTmpFrame;
DWORD dwCurrentLead = 0;
DWORD dwAllowedLeadBuffers = 0;
DWORD dwAllowedLeadBytes = 0;
DWORD dwHalfBufferSize = 0;
BOOL fDesktopCurrent = TRUE;
dvMsgPlayerVoiceStart.dwSize = sizeof( DVMSG_PLAYERVOICESTART );
dvMsgPlayerVoiceStop.dwSize = sizeof( DVMSG_PLAYERVOICESTOP );
CDirectVoiceClientEngine *This = (CDirectVoiceClientEngine *) lParam;
// Pre-calc some sizes..
dwAllowedLeadBuffers = DV_CLIENT_BASE_LEAD_MAX;
dwAllowedLeadBytes = This->m_dwUnCompressedFrameSize * DV_CLIENT_BASE_LEAD_MAX;
dwHalfBufferSize = This->m_dwUnCompressedFrameSize * (This->m_dwNumPerBuffer/2);
hr = COM_CoInitialize(NULL);
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error initializing COM on playback thread" );
This->HandleThreadError( DVERR_GENERIC );
goto EXIT_PLAYBACK;
}
lpSourceBuffer = new BYTE[This->m_dwUnCompressedFrameSize];
if( lpSourceBuffer == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to allocate source buffer" );
This->HandleThreadError( DVERR_OUTOFMEMORY );
goto EXIT_PLAYBACK;
}
hEventArray[0] = This->m_hPlaybackTerminate;
hEventArray[1] = This->m_thTimerInfo.hPlaybackTimerEvent;
if( hEventArray[0] == NULL || hEventArray[1] == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to initialize events" );
This->HandleThreadError( DVERR_GENERIC );
goto EXIT_PLAYBACK;
}
if( This->m_audioPlaybackDevice->IsEmulated() )
{
DPFX(DPFPREP, DVF_WARNINGLEVEL, "WARNING: DirectSound is in emulated mode" );
dwAllowedLeadBuffers += DV_CLIENT_EMULATED_LEAD_ADJUST;
dwAllowedLeadBytes += DV_CLIENT_EMULATED_LEAD_ADJUST * This->m_dwUnCompressedFrameSize;
}
else
{
DPFX(DPFPREP, DVF_WARNINGLEVEL, "NOTE: DirectSound is in standard mode" );
}
while( 1 )
{
lWaitResult = WaitForMultipleObjects( 2, hEventArray, FALSE, INFINITE );
if( lWaitResult == WAIT_OBJECT_0 )
{
break;
}
hr = This->m_lpstGeneralBuffer->GetCurrentLead( &dwCurrentLead );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to get current position of main buff hr=0x%x", hr );
This->HandleThreadError( hr );
goto EXIT_PLAYBACK;
}
while( 1 )
{
if( dwCurrentLead < (dwAllowedLeadBytes) )
{
DPFX(DPFPREP, DVF_INFOLEVEL, "PWI, Lead: %d Running a pass..", dwCurrentLead );
}
else if( dwCurrentLead > dwHalfBufferSize )
{
DPFX(DPFPREP, DVF_INFOLEVEL, "PWI, Lead: %d Running a pass (wraparound)..", dwCurrentLead );
}
else
{
DPFX(DPFPREP, DVF_INFOLEVEL, "PWI, Lead: %d NOT Running a pass..", dwCurrentLead );
break;
}
DNEnterCriticalSection( &This->m_thTimerInfo.csPlayCount );
// Only run a max of two times per iteration
if( This->m_thTimerInfo.lPlaybackCount > dwAllowedLeadBuffers )
This->m_thTimerInfo.lPlaybackCount = dwAllowedLeadBuffers;
else
This->m_thTimerInfo.lPlaybackCount--;
DNLeaveCriticalSection( &This->m_thTimerInfo.csPlayCount );
#ifdef WINNT
fDesktopCurrent = ( USER_SHARED_DATA->ActiveConsoleId == NtCurrentPeb()->SessionId);
#endif
bHighPeak = 0;
// Update list
This->UpdateActivePlayPendingList( );
if( This->m_dvClientConfig.dwFlags & DVCLIENTCONFIG_ECHOSUPPRESSION )
{
DNEnterCriticalSection( &This->m_lockPlaybackMode );
dwEchoState = This->m_dwEchoState;
DNLeaveCriticalSection( &This->m_lockPlaybackMode );
}
else
{
dwEchoState = DVCECHOSTATE_IDLE;
}
if( dwEchoState != DVCECHOSTATE_RECORDING )
{
dwCurrentTime = GetTickCount();
pblSearch = This->m_blPlayActivePlayers.GetNext();
while( pblSearch != &This->m_blPlayActivePlayers )
{
pCurrentPlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blPlayList );
ASSERT_VPLAYER(pCurrentPlayer);
pblSearch = pblSearch->GetNext();
dwResultSize = This->m_dwUnCompressedFrameSize;
dwCompressStart = GetTickCount();
if( !pCurrentPlayer->IsInBoundConverterInitialized() )
{
hr = pCurrentPlayer->CreateInBoundConverter( This->m_lpdvfCompressionInfo->guidType, s_lpwfxPlaybackFormat );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to get create converter for player hr=0x%x", hr );
This->HandleThreadError( DVERR_GENERIC );
goto EXIT_PLAYBACK;
}
}
if( This->m_dvClientConfig.dwFlags & DVCLIENTCONFIG_PLAYBACKMUTE ||
!fDesktopCurrent )
{
frTmpFrame = pCurrentPlayer->Dequeue(&fLost, &fSilence);
frTmpFrame->Return();
continue;
}
hr = pCurrentPlayer->GetNextFrameAndDecompress( lpSourceBuffer, &dwResultSize, &fLost, &fSilence, &dwSeqNum, &dwMsgNum );
// If bad speech packet is constructed so it caused compressor to fail, it's ignored.
// Old code dropped us from the session, which would allow remote clients to drop
// us with a single packet.
if( FAILED( hr ) )
{
dwSeqNum = 0;
dwMsgNum = 0;
fLost = FALSE;
fSilence = TRUE;
}
dwCompressTime = GetTickCount() - dwCompressStart;
// STATSBLOCK: Begin
This->m_pStatsBlob->m_dwPDTTotal += dwCompressTime;
if( dwCompressTime < This->m_pStatsBlob->m_dwPDTMin )
{
This->m_pStatsBlob->m_dwPDTMin = dwCompressTime;
}
if( dwCompressTime > This->m_pStatsBlob->m_dwPDTMax )
{
This->m_pStatsBlob->m_dwPDTMax = dwCompressTime;
}
// STATSBLOCK: End
// STATSBLOCK: Begin
if( fLost )
{
DPFX(DPFPREP, DVF_CLIENT_SEQNUM_DEBUG_LEVEL, "SEQ: Dequeue: Lost Frame" );
DPFX(DPFPREP, DVF_GLITCH_DEBUG_LEVEL, "GLITCH: Dequeue: Packet was lost. Speech gap will occur." );
This->m_pStatsBlob->m_dwPPDQLost++;
}
else if( fSilence )
{
DPFX(DPFPREP, DVF_CLIENT_SEQNUM_DEBUG_LEVEL, "SEQ: Dequeue: Silent Frame" );
This->m_pStatsBlob->m_dwPPDQSilent++;
}
else
{
DPFX(DPFPREP, DVF_CLIENT_SEQNUM_DEBUG_LEVEL, "SEQ: Dequeue: Msg [%d] Seq [%d]", dwMsgNum, dwSeqNum );
This->m_pStatsBlob->m_dwPPDQSpeech++;
}
// STATSBLOCK: End
// If the player sent us silence, increment the silent count
if( fSilence )
{
// If we're receiving on this user
if( pCurrentPlayer->IsReceiving() )
{
// If it exceeds the max.
if( (dwCurrentTime - pCurrentPlayer->GetLastPlayback()) > PLAYBACK_RECEIVESTOP_TIMEOUT )
{
pCurrentPlayer->SetReceiving(FALSE);
dvMsgPlayerVoiceStop.dwSize = sizeof( dvMsgPlayerVoiceStop );
dvMsgPlayerVoiceStop.dvidSourcePlayerID = pCurrentPlayer->GetPlayerID();
dvMsgPlayerVoiceStop.pvPlayerContext = pCurrentPlayer->GetContext();
This->NotifyQueue_Add( DVMSGID_PLAYERVOICESTOP, &dvMsgPlayerVoiceStop, sizeof( dvMsgPlayerVoiceStop ) );
This->m_dwActiveCount--;
if( This->m_dwActiveCount == 0 )
{
if( This->m_dvClientConfig.dwFlags & DVCLIENTCONFIG_ECHOSUPPRESSION )
{
DNEnterCriticalSection( &This->m_lockPlaybackMode );
if( This->m_dwEchoState == DVCECHOSTATE_PLAYBACK )
{
DPFX(DPFPREP, PLAYBACK_SWITCH_DEBUG_LEVEL, "%%%% Switching to idle mode" );
This->m_dwEchoState = DVCECHOSTATE_IDLE;
}
DNLeaveCriticalSection( &This->m_lockPlaybackMode );
}
}
}
}
}
else
{
// We receive data and this is the first one
if( !pCurrentPlayer->IsReceiving() )
{
dvMsgPlayerVoiceStart.dvidSourcePlayerID = pCurrentPlayer->GetPlayerID();
dvMsgPlayerVoiceStart.pvPlayerContext = pCurrentPlayer->GetContext();
This->NotifyQueue_Add( DVMSGID_PLAYERVOICESTART, &dvMsgPlayerVoiceStart, sizeof(DVMSG_PLAYERVOICESTART) );
This->m_dwActiveCount++;
if( This->m_dwActiveCount == 1 )
{
if( This->m_dvClientConfig.dwFlags & DVCLIENTCONFIG_ECHOSUPPRESSION )
{
DNEnterCriticalSection( &This->m_lockPlaybackMode );
if( This->m_dwEchoState == DVCECHOSTATE_IDLE )
{
DPFX(DPFPREP, PLAYBACK_SWITCH_DEBUG_LEVEL, "%%%% Switching to playback mode" );
This->m_dwEchoState = DVCECHOSTATE_PLAYBACK;
}
DNLeaveCriticalSection( &This->m_lockPlaybackMode );
}
}
pCurrentPlayer->SetReceiving(TRUE);
}
}
fMixed = FALSE;
// If the frame was not silence, decompress it and then
// mix it into the mixer buffer
if( !fSilence &&
fDesktopCurrent &&
!(This->m_dvClientConfig.dwFlags & DVCLIENTCONFIG_PLAYBACKMUTE)
)
{
DPFX(DPFPREP, DVF_INFOLEVEL, "Player: 0x%x getting frame.. it's speech", pCurrentPlayer->GetPlayerID() );
// Lock the buffer list
DNEnterCriticalSection( &This->m_csBufferLock );
lpstCurrent = This->m_lpstBufferList;
if( pCurrentPlayer->GetLastPeak() > bHighPeak )
{
bHighPeak = pCurrentPlayer->GetLastPeak();
}
// Loop through list looking for buffers to mix into
while( lpstCurrent != NULL )
{
if( lpstCurrent->GetTarget() == pCurrentPlayer->GetPlayerID() )
{
lpstCurrent->MixInSingle( lpSourceBuffer );
fMixed = TRUE;
}
else if( This->m_lpSessionTransport->IsPlayerInGroup( lpstCurrent->GetTarget(), pCurrentPlayer->GetPlayerID() ) )
{
lpstCurrent->MixIn( lpSourceBuffer );
fMixed = TRUE;
}
lpstCurrent = lpstCurrent->m_lpstNext;
}
DNLeaveCriticalSection( &This->m_csBufferLock );
if( !(This->m_dvClientConfig.dwFlags & DVCLIENTCONFIG_MUTEGLOBAL) )
{
// If we didn't mix into any user created buffers, then
// Mix into the main buffer
if( !fMixed )
{
if( This->m_dwPlayActiveCount == 1 )
{
hr = This->m_lpstGeneralBuffer->MixInSingle( lpSourceBuffer );
}
else
{
hr = This->m_lpstGeneralBuffer->MixIn( lpSourceBuffer );
}
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to do mix." );
DNLeaveCriticalSection( &This->m_csBufferLock );
This->HandleThreadError( hr );
goto EXIT_PLAYBACK;
}
}
}
}
else
{
DPFX(DPFPREP, DVF_INFOLEVEL, "Player: 0x%x getting frame.. it's silence", pCurrentPlayer->GetPlayerID() );
}
} // for each player
} // not in the recording state and at least one talking
else
{
bHighPeak = 0;
}
// Lock the buffer list
DNEnterCriticalSection( &This->m_csBufferLock );
lpstCurrent = This->m_lpstBufferList;
#if defined(DEBUG) || defined(DBG)
This->CHECKLISTINTEGRITY();
#endif
// Loop through list looking for buffers to mix into
while( lpstCurrent != NULL )
{
#if defined(DEBUG) || defined(DBG)
This->CHECKLISTINTEGRITY();
#endif
hr = lpstCurrent->Commit();
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error commiting to buffer hr=0x%x -- Locked by user?", hr );
DNLeaveCriticalSection( &This->m_csBufferLock );
if( hr == DSERR_NODRIVER )
{
hr = DVERR_INVALIDDEVICE;
}
else
{
hr = DVERR_LOCKEDBUFFER;
}
This->HandleThreadError( hr );
goto EXIT_PLAYBACK;
}
lpstCurrent = lpstCurrent->m_lpstNext;
}
This->m_bLastPlaybackPeak = bHighPeak;
hr = This->m_lpstGeneralBuffer->Commit();
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error commiting to buffer hr=0x%x -- Locked by user?", hr );
DNLeaveCriticalSection( &This->m_csBufferLock );
if( hr == DSERR_NODRIVER )
{
hr = DVERR_INVALIDDEVICE;
}
else
{
hr = DVERR_LOCKEDBUFFER;
}
This->HandleThreadError( hr );
goto EXIT_PLAYBACK;
}
DNLeaveCriticalSection( &This->m_csBufferLock );
hr = This->m_lpstGeneralBuffer->GetCurrentLead( &dwCurrentLead );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to get current pos of main buffer hr=0x%x", hr );
This->HandleThreadError( hr );
goto EXIT_PLAYBACK;
}
}
}
EXIT_PLAYBACK:
DPFX(DPFPREP, DVF_INFOLEVEL, "PT: Exiting" );
// Stop the playback
//This->m_audioPlaybackBuffer->Stop();
// Deallocate the buffers
DPFX(DPFPREP, DVF_INFOLEVEL, "PT: mixer gone" );
if( lpSourceBuffer != NULL )
{
delete [] lpSourceBuffer;
}
DPFX(DPFPREP, DVF_INFOLEVEL, "PT: source gone" );
// Signal that thread is done.
SetEvent( This->m_hPlaybackDone );
DPFX(DPFPREP, DVF_INFOLEVEL, "PT: Shutdown complete" );
COM_CoUninitialize();
_endthread();
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::SetPlaybackVolume"
//
// SetPlaybackVolume
//
// Sets the playback volume of all the playback buffers
//
HRESULT CDirectVoiceClientEngine::SetPlaybackVolume( LONG lVolume )
{
CSoundTarget *lpstCurrent;
CAudioPlaybackBuffer *lpdsBuffer;
m_audioPlaybackBuffer->SetVolume( lVolume );
// Lock the buffer list
DNEnterCriticalSection( &m_csBufferLock );
lpstCurrent = m_lpstBufferList;
// Loop through list of buffers setting the volume
while( lpstCurrent != NULL )
{
lpdsBuffer = lpstCurrent->GetBuffer();
if( lpdsBuffer != NULL )
{
lpdsBuffer->SetVolume( lVolume );
}
lpstCurrent = lpstCurrent->m_lpstNext;
}
// Lock the buffer list
DNLeaveCriticalSection( &m_csBufferLock );
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::HandleThreadError"
//
// HandleThreadError
//
// Handles errors within threads. When an error occurs this function sets the
// session lost flag and notifies the notifythread to perform a disconnect.
//
void CDirectVoiceClientEngine::HandleThreadError( HRESULT hrResult )
{
DoSessionLost( hrResult );
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::RecordThread"
// RecordThread
//
// Thread to handle compression / transmission
//
void CDirectVoiceClientEngine::RecordThread( void *lpParam )
{
CDirectVoiceClientEngine *This = (CDirectVoiceClientEngine *) lpParam;
DNASSERT( This != NULL );
DPFX(DPFPREP, DVF_ENTRYLEVEL, "DVCE::RecordThread() Begin" );
HANDLE hEventArray[2];
LONG lWaitResult;
HRESULT hr;
BOOL fContinue = FALSE;
DWORD dwNumRunsPerWakeup = 0;
CClientRecordSubSystem *subSystem = NULL;
hr = COM_CoInitialize(NULL);
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error innitializing COM on record thread" );
goto EXIT_ERROR;
}
subSystem = new CClientRecordSubSystem( This );
// Check that the converter is valid
if( subSystem == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Memory alloc failure" );
goto EXIT_ERROR;
}
hr = subSystem->Initialize();
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Record Sub Error during init hr=0x%x", hr );
goto EXIT_ERROR;
}
hEventArray[0] = This->m_hRecordTerminate;
hEventArray[1] = This->m_thTimerInfo.hRecordTimerEvent;
// Loop while we're connected
while( 1 )
{
lWaitResult = WaitForMultipleObjects( 2, hEventArray, FALSE, INFINITE );
This->m_pStatsBlob->m_recStats.m_dwNumWakeups++;
if( lWaitResult == WAIT_OBJECT_0 )
{
break;
}
fContinue = TRUE;
dwNumRunsPerWakeup = 0;
while( fContinue )
{
// Wait for next frame, if this fails recording
// system is locked. Return
hr = subSystem->GetNextFrame( &fContinue );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to retrieve next frame hr = 0x%x", hr );
goto EXIT_ERROR;
}
if( !fContinue )
{
ResetEvent( This->m_thTimerInfo.hRecordTimerEvent );
break;
}
This->m_pStatsBlob->m_recStats.m_dwRPWTotal++;
dwNumRunsPerWakeup++;
DPFX(DPFPREP, DVF_INFOLEVEL, "> RTAR: Got" );
hr = subSystem->RecordFSM();
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to update FSM hr=0x%x", hr );
goto EXIT_ERROR;
}
DPFX(DPFPREP, DVF_INFOLEVEL, "> RTAR: FSM" );
// Transmit the frame if need be.
hr = subSystem->TransmitFrame();
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "TransmitFrame Failed hr=0x%x", hr );
goto EXIT_ERROR;
}
DPFX(DPFPREP, DVF_INFOLEVEL, "> RTAR: Trans" );
}
if( dwNumRunsPerWakeup < This->m_pStatsBlob->m_recStats.m_dwRPWMin )
{
This->m_pStatsBlob->m_recStats.m_dwRPWMin = dwNumRunsPerWakeup;
}
if( dwNumRunsPerWakeup > This->m_pStatsBlob->m_recStats.m_dwRPWMax )
{
This->m_pStatsBlob->m_recStats.m_dwRPWMax = dwNumRunsPerWakeup;
}
}
// Delete the recording subsystem
delete subSystem;
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Record Sub Gone" );
COM_CoUninitialize();
SetEvent( This->m_hRecordDone );
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Record Gone" );
return;
EXIT_ERROR:
if( subSystem )
delete subSystem;
This->HandleThreadError( DVERR_RECORDSYSTEMERROR );
COM_CoUninitialize();
SetEvent( This->m_hRecordDone );
_endthread();
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::MigrateHost"
// MigrateHost
//
// This function is called by DV_HostMigrate when the local client has received a host
// migration notification.
//
//
HRESULT CDirectVoiceClientEngine::MigrateHost( DVID dvidNewHost, LPDIRECTPLAYVOICESERVER lpdvServer )
{
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::NotifyQueue_Flush"
//
// NotifyQueue_Flush
//
// This function does not return until all notifications have been indicated
//
void CDirectVoiceClientEngine::NotifyQueue_Flush()
{
BFCSingleLock slLock( &m_csNotifyQueueLock );
slLock.Lock();
while( m_lpNotifyList )
{
NotifyQueue_IndicateNext();
}
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::NotifyQueue_IndicateNext"
HRESULT CDirectVoiceClientEngine::NotifyQueue_IndicateNext()
{
HRESULT hr;
CNotifyElement *neElement;
hr = NotifyQueue_Get( &neElement );
if( FAILED( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "NotifyQueue_Get Failed hr=0x%x", hr );
}
else
{
DPFX(DPFPREP, DVF_INFOLEVEL, "Sending notification type=0x%x", neElement->m_dwType );
if( neElement->m_etElementType == NOTIFY_FIXED )
{
TransmitMessage( neElement->m_dwType,
&neElement->m_element.fixed,
neElement->m_dwDataSize );
}
else
{
TransmitMessage( neElement->m_dwType,
neElement->m_element.dynamic.m_lpData,
neElement->m_dwDataSize );
}
DPFX(DPFPREP, DVF_INFOLEVEL, "Returning notification" );
NotifyQueue_ElementFree( neElement );
}
return hr;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::NotifyQueue_Add"
// Queue up a notification for the user
HRESULT CDirectVoiceClientEngine::NotifyQueue_Add( DWORD dwMessageType, LPVOID lpData, DWORD dwDataSize, PVOID pvContext, CNotifyElement::PNOTIFY_COMPLETE pNotifyFunc )
{
BFCSingleLock slLock( &m_csNotifyQueueLock );
slLock.Lock();
if( !m_fNotifyQueueEnabled )
{
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Ignoring indication, queue disabled" );
}
CNotifyElement *lpNewElement;
HRESULT hr;
lpNewElement = (CNotifyElement *) m_fpNotifications.Get( );
if( lpNewElement == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to get a block for a notifier" );
return DVERR_OUTOFMEMORY;
}
// Setup the notification callbacks, if there are ones
lpNewElement->pvContext = pvContext;
lpNewElement->pNotifyFunc = pNotifyFunc;
if( dwMessageType == DVMSGID_PLAYERVOICESTOP )
{
const DVMSG_PLAYERVOICESTOP* pMsgStop = (PDVMSG_PLAYERVOICESTOP) lpData;
DNASSERT( pMsgStop->dwSize == sizeof( DVMSG_PLAYERVOICESTOP ) );
DNASSERT( dwDataSize == sizeof( DVMSG_PLAYERVOICESTOP ) );
}
if( dwMessageType == DVMSGID_PLAYERVOICESTART )
{
const DVMSG_PLAYERVOICESTART* pMsgStart = (PDVMSG_PLAYERVOICESTART) lpData;
DNASSERT( pMsgStart->dwSize == sizeof( DVMSG_PLAYERVOICESTART ) );
DNASSERT( dwDataSize == sizeof( DVMSG_PLAYERVOICESTART ) );
}
if( dwMessageType == DVMSGID_PLAYEROUTPUTLEVEL )
{
const DVMSG_PLAYEROUTPUTLEVEL* pMsgOutput = (PDVMSG_PLAYEROUTPUTLEVEL) lpData;
DNASSERT( pMsgOutput->dwSize == sizeof( DVMSG_PLAYEROUTPUTLEVEL ) );
DNASSERT( dwDataSize == sizeof( DVMSG_PLAYEROUTPUTLEVEL ) );
}
lpNewElement->m_lpNext = m_lpNotifyList;
lpNewElement->m_dwType = dwMessageType;
lpNewElement->m_dwDataSize = dwDataSize;
if( dwDataSize <= DV_CLIENT_NOTIFY_ELEMENT_SIZE )
{
lpNewElement->m_etElementType = NOTIFY_FIXED;
}
else
{
lpNewElement->m_etElementType = NOTIFY_DYNAMIC;
}
if( lpNewElement->m_etElementType == NOTIFY_FIXED )
{
memcpy( &lpNewElement->m_element.fixed, lpData, dwDataSize );
}
else if( lpNewElement->m_etElementType == NOTIFY_DYNAMIC )
{
lpNewElement->m_element.dynamic.m_lpData = new BYTE[dwDataSize];
if( lpNewElement->m_element.dynamic.m_lpData == NULL )
{
m_fpNotifications.Release( lpNewElement );
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to alloc memory for notification" );
return DVERR_OUTOFMEMORY;
}
memcpy( lpNewElement->m_element.dynamic.m_lpData, lpData, dwDataSize );
}
else
{
DNASSERT( FALSE );
}
// Fixups for internal pointers
//
// Required for certain message types (currently only DVMSGID_SETTARGETS)
if( dwMessageType == DVMSGID_SETTARGETS )
{
PDVMSG_SETTARGETS pdvSetTarget;
if( lpNewElement->m_etElementType == NOTIFY_FIXED )
{
pdvSetTarget = (PDVMSG_SETTARGETS) &lpNewElement->m_element.fixed;
}
else
{
pdvSetTarget = (PDVMSG_SETTARGETS) lpNewElement->m_element.dynamic.m_lpData;
}
pdvSetTarget->pdvidTargets = (PDVID) &pdvSetTarget[1];
lpNewElement->m_dwDataSize = sizeof( DVMSG_SETTARGETS );
}
// We're ignoring this notification, call the completion immediately.
if( !m_fNotifyQueueEnabled )
{
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Ignoring notification, calling completion immediately" );
NotifyQueue_ElementFree( lpNewElement );
return DV_OK;
}
m_lpNotifyList = lpNewElement;
ReleaseSemaphore( m_hNewNotifyElement, 1, NULL );
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::NotifyQueue_Get"
// Retrieve the next element in our queue.
//
// If there is an element, return DV_OK, otherwise DVERR_GENERIC.
//
HRESULT CDirectVoiceClientEngine::NotifyQueue_Get( CNotifyElement **pneElement )
{
BFCSingleLock slLock( &m_csNotifyQueueLock );
slLock.Lock();
CNotifyElement *lpIterator = m_lpNotifyList;
CNotifyElement *lpTrailIterator = NULL;
if( lpIterator == NULL )
return DVERR_GENERIC;
// Move forward to the last element in the list
while( lpIterator->m_lpNext != NULL )
{
lpTrailIterator = lpIterator;
lpIterator = lpIterator->m_lpNext;
}
*pneElement = lpIterator;
// Remove the last element in the list
// Special case, only element on the list
if( lpTrailIterator == NULL )
{
m_lpNotifyList = NULL;
}
else
{
lpTrailIterator->m_lpNext = NULL;
}
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::NotifyQueue_Init"
HRESULT CDirectVoiceClientEngine::NotifyQueue_Init()
{
BFCSingleLock slLock( &m_csNotifyQueueLock );
slLock.Lock();
if (!m_fpNotifications.Initialize( sizeof( CNotifyElement ), NULL, NULL, NULL, NULL ))
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to create fixed pool for notify elements" );
return DVERR_OUTOFMEMORY;
}
m_lpNotifyList = NULL;
m_hNewNotifyElement = CreateSemaphore( NULL, 0, DVCLIENT_NOTIFY_MAXSEMCOUNT, NULL );
if (m_hNewNotifyElement==NULL)
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to create semaphore for notify elements" );
return DVERR_GENERIC;
}
m_fNotifyQueueEnabled = TRUE;
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::NotifyQueue_Enable"
void CDirectVoiceClientEngine::NotifyQueue_Enable()
{
BFCSingleLock slLock( &m_csNotifyQueueLock );
slLock.Lock();
m_fNotifyQueueEnabled = TRUE;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::NotifyQueue_Disable"
void CDirectVoiceClientEngine::NotifyQueue_Disable()
{
BFCSingleLock slLock( &m_csNotifyQueueLock );
slLock.Lock();
m_fNotifyQueueEnabled = FALSE;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::NotifyQueue_ElementFree"
HRESULT CDirectVoiceClientEngine::NotifyQueue_ElementFree( CNotifyElement *lpElement )
{
// Call the notification function, if there is one
if( lpElement->pNotifyFunc )
{
(*lpElement->pNotifyFunc)(lpElement->pvContext,lpElement);
lpElement->pNotifyFunc = NULL;
lpElement->pvContext = NULL;
}
// If this element is dynamic free the associated memory
if( lpElement->m_etElementType == NOTIFY_DYNAMIC )
{
delete [] lpElement->m_element.dynamic.m_lpData;
}
// Return notifier to the fixed pool manager
m_fpNotifications.Release( lpElement );
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::NotifyQueue_Free"
HRESULT CDirectVoiceClientEngine::NotifyQueue_Free()
{
BFCSingleLock slLock( &m_csNotifyQueueLock );
slLock.Lock();
CNotifyElement *lpTmpElement;
CNotifyElement *lpIteratorElement;
lpIteratorElement = m_lpNotifyList;
while( lpIteratorElement != NULL )
{
lpTmpElement = lpIteratorElement;
lpIteratorElement = lpIteratorElement->m_lpNext;
NotifyQueue_ElementFree( lpTmpElement );
}
if( m_hNewNotifyElement != NULL )
{
CloseHandle( m_hNewNotifyElement );
}
if (m_fNotifyQueueEnabled)
{
m_fpNotifications.DeInitialize();
}
return DV_OK;
}
//
// HANDLERS FOR WHEN WE HANDLE REMOTE SERVERS
//
//
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::CreateGroup"
HRESULT CDirectVoiceClientEngine::CreateGroup( DVID dvID )
{
return S_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::DeleteGroup"
HRESULT CDirectVoiceClientEngine::DeleteGroup( DVID dvID )
{
CheckForAndRemoveTarget( dvID );
// If there are any buffers for this player, delete them
//
// Leave the buffer around so the user can call Delete3DSoundBuffer
//
// DeleteSoundTarget( dvID );
return S_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::AddPlayerToGroup"
HRESULT CDirectVoiceClientEngine::AddPlayerToGroup( DVID dvidGroup, DVID dvidPlayer )
{
return S_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::RemovePlayerFromGroup"
HRESULT CDirectVoiceClientEngine::RemovePlayerFromGroup( DVID dvidGroup, DVID dvidPlayer )
{
return S_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::DoSessionLost"
void CDirectVoiceClientEngine::DoSessionLost( HRESULT hrDisconnectResult )
{
if( GetCurrentState() == DVCSTATE_CONNECTED )
{
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "#### SESSION LOST [hr=0x%x]", hrDisconnectResult );
m_fSessionLost = TRUE;
DoSignalDisconnect( hrDisconnectResult );
}
else if( GetCurrentState() == DVCSTATE_DISCONNECTING )
{
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "#### Received session lost during disconnect, completing disconnect" );
DoSignalDisconnect( m_hrDisconnectResult );
}
else
{
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "#### Received session lost but not connected or disconnecting, ignoring" );
}
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::DoSignalDisconnect"
void CDirectVoiceClientEngine::DoSignalDisconnect( HRESULT hrDisconnectResult )
{
DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "#### Disconnecting [hr=0x%x]", hrDisconnectResult );
m_hrDisconnectResult = hrDisconnectResult;
SetEvent( m_hNotifyDisconnect );
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::ClientStats_Reset"
void CDirectVoiceClientEngine::ClientStats_Reset()
{
memset( &m_stats, 0x00, sizeof( ClientStatistics ) );
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::ClientStats_Dump_Record"
void CDirectVoiceClientEngine::ClientStats_Dump_Record()
{
if( m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_HALFDUPLEX )
{
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Session was half duplex, no record stats available" );
return;
}
char tmpBuffer[200];
DWORD dwRecRunLength = m_pStatsBlob->m_recStats.m_dwTimeStop - m_pStatsBlob->m_recStats.m_dwTimeStart;
if( dwRecRunLength == 0 )
dwRecRunLength = 1;
DWORD dwNumInternalRuns = m_pStatsBlob->m_recStats.m_dwNumWakeups;
if( dwNumInternalRuns == 0 )
dwNumInternalRuns = 1;
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Start Lag (ms) : %u", m_pStatsBlob->m_recStats.m_dwStartLag );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Record Run Length (ms) : %u", dwRecRunLength );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Speech Size (Uncomp.) : %u", m_pStatsBlob->m_recStats.m_dwUnCompressedSize );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Frames / Buffer : %u", m_pStatsBlob->m_recStats.m_dwFramesPerBuffer );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Time / Frame (ms) : %u", m_pStatsBlob->m_recStats.m_dwFrameTime );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Silence Timeout : %u", m_pStatsBlob->m_recStats.m_dwSilenceTimeout );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "# of wakeups : %u", m_pStatsBlob->m_recStats.m_dwNumWakeups );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Runs / Wakeup : Avg: %u [%u..%u]",
m_pStatsBlob->m_recStats.m_dwRPWTotal / dwNumInternalRuns,
m_pStatsBlob->m_recStats.m_dwRPWMin,
m_pStatsBlob->m_recStats.m_dwRPWMax );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "# of messages : %u", m_pStatsBlob->m_recStats.m_dwNumMessages );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Sent Frames : %u", m_pStatsBlob->m_recStats.m_dwSentFrames );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Ignored Frames : %u", m_pStatsBlob->m_recStats.m_dwIgnoredFrames );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Message Length (frames): Avg: %u [%u..%u]",
(m_pStatsBlob->m_recStats.m_dwNumMessages == 0) ? 0 : m_pStatsBlob->m_recStats.m_dwMLTotal / m_pStatsBlob->m_recStats.m_dwNumMessages,
m_pStatsBlob->m_recStats.m_dwMLMin,
m_pStatsBlob->m_recStats.m_dwMLMax );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Header Size (bytes) : Avg: %u [%u..%u]",
(m_pStatsBlob->m_recStats.m_dwSentFrames == 0) ? 0 : m_pStatsBlob->m_recStats.m_dwHSTotal / m_pStatsBlob->m_recStats.m_dwSentFrames,
m_pStatsBlob->m_recStats.m_dwHSMin,
m_pStatsBlob->m_recStats.m_dwHSMax );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Speech Size (bytes) : Avg: %u [%u..%u]",
(m_pStatsBlob->m_recStats.m_dwSentFrames == 0) ? 0 : m_pStatsBlob->m_recStats.m_dwCSTotal / m_pStatsBlob->m_recStats.m_dwSentFrames,
m_pStatsBlob->m_recStats.m_dwCSMin,
m_pStatsBlob->m_recStats.m_dwCSMax );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Speech Convert (ms) : Avg: %u [%u..%u]",
(m_pStatsBlob->m_recStats.m_dwSentFrames == 0) ? 0 : (m_pStatsBlob->m_recStats.m_dwCTTotal / m_pStatsBlob->m_recStats.m_dwSentFrames),
m_pStatsBlob->m_recStats.m_dwCTMin,
m_pStatsBlob->m_recStats.m_dwCTMax );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Record Resets : %u [Before Success %u..%u]", m_pStatsBlob->m_recStats.m_dwRRTotal,
m_pStatsBlob->m_recStats.m_dwRRMin,
m_pStatsBlob->m_recStats.m_dwRRMax );
sprintf( tmpBuffer, "Rec Movement (ms) : Avg: %u [%u..%u]",
m_pStatsBlob->m_recStats.m_dwRMMSTotal / dwNumInternalRuns,
m_pStatsBlob->m_recStats.m_dwRMMSMin,
m_pStatsBlob->m_recStats.m_dwRMMSMax );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
sprintf( tmpBuffer, "Rec Movement (frames) : Avg: %.2f [%.2f..%.2f]",
( ((float)m_pStatsBlob->m_recStats.m_dwRMMSTotal) / ((float) dwNumInternalRuns)) / ((float) m_pStatsBlob->m_recStats.m_dwFrameTime),
((float) m_pStatsBlob->m_recStats.m_dwRMMSMin) / ((float) m_pStatsBlob->m_recStats.m_dwFrameTime),
((float) m_pStatsBlob->m_recStats.m_dwRMMSMax) / ((float) m_pStatsBlob->m_recStats.m_dwFrameTime));
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
sprintf( tmpBuffer, "Rec Movement (bytes) : Avg: %u [%u..%u]",
m_pStatsBlob->m_recStats.m_dwRMBTotal / dwNumInternalRuns,
m_pStatsBlob->m_recStats.m_dwRMBMin,
m_pStatsBlob->m_recStats.m_dwRMBMax );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
sprintf( tmpBuffer, "Rec Movement (frames) : Avg: %.2f [%.2f..%.2f]",
(((float) m_pStatsBlob->m_recStats.m_dwRMBTotal) / ((float) dwNumInternalRuns)) / ((float) m_pStatsBlob->m_recStats.m_dwUnCompressedSize),
((float) m_pStatsBlob->m_recStats.m_dwRMBMin) / ((float) m_pStatsBlob->m_recStats.m_dwUnCompressedSize),
((float) m_pStatsBlob->m_recStats.m_dwRMBMax) / ((float) m_pStatsBlob->m_recStats.m_dwUnCompressedSize) );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
sprintf( tmpBuffer, "Move Delta (ms) : Avg: %u [%u..%u]",
m_pStatsBlob->m_recStats.m_dwRTSLMTotal / dwNumInternalRuns,
m_pStatsBlob->m_recStats.m_dwRTSLMMin,
m_pStatsBlob->m_recStats.m_dwRTSLMMax );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
sprintf( tmpBuffer, "Move Delta (frames) : Avg: %.2f [%.2f..%.2f]",
(((float) m_pStatsBlob->m_recStats.m_dwRTSLMTotal) / ((float) dwNumInternalRuns)) / ((float) m_pStatsBlob->m_recStats.m_dwUnCompressedSize),
((float) m_pStatsBlob->m_recStats.m_dwRTSLMMin) / ((float) m_pStatsBlob->m_recStats.m_dwUnCompressedSize),
((float) m_pStatsBlob->m_recStats.m_dwRTSLMMax) / ((float) m_pStatsBlob->m_recStats.m_dwUnCompressedSize) );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
sprintf( tmpBuffer, "Record Lag (bytes) : Avg: %u [%u..%u]",
m_pStatsBlob->m_recStats.m_dwRLTotal / dwNumInternalRuns,
m_pStatsBlob->m_recStats.m_dwRLMin ,
m_pStatsBlob->m_recStats.m_dwRLMax );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
sprintf( tmpBuffer, "Record Lag (frames) : Avg: %.2f [%.2f..%.2f]",
(float) ((float) m_pStatsBlob->m_recStats.m_dwRLTotal / (float) dwNumInternalRuns) / ((float) m_pStatsBlob->m_recStats.m_dwUnCompressedSize),
(float) ((float) m_pStatsBlob->m_recStats.m_dwRLMin) / (float) m_pStatsBlob->m_recStats.m_dwUnCompressedSize,
(float) ((float) m_pStatsBlob->m_recStats.m_dwRLMax) / (float) m_pStatsBlob->m_recStats.m_dwUnCompressedSize );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::ClientStats_Dump_Playback"
void CDirectVoiceClientEngine::ClientStats_Dump_Playback()
{
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Playback Stats: " );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Frames (silent) : %d", m_pStatsBlob->m_dwPPDQSilent );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Frames (lost) : %d", m_pStatsBlob->m_dwPPDQLost );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Frames (speech) : %d", m_pStatsBlob->m_dwPPDQSpeech );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Speech Decompress (ms) : Avg: %u [%u..%u]",
(m_pStatsBlob->m_dwPPDQSpeech == 0) ? 0 : (m_pStatsBlob->m_dwPDTTotal / m_pStatsBlob->m_dwPPDQSpeech),
m_pStatsBlob->m_dwPDTMin,
m_pStatsBlob->m_dwPDTMax );
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::ClientStats_Dump_Receive"
void CDirectVoiceClientEngine::ClientStats_Dump_Receive()
{
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Receive Stats: " );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Received Frames : %d", m_pStatsBlob->m_dwPRESpeech );
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Received But Lost : %d", m_pStatsBlob->m_dwPRESpeech - m_pStatsBlob->m_dwPPDQSpeech );
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::ClientStats_Dump_Transmit"
void CDirectVoiceClientEngine::ClientStats_Dump_Transmit()
{
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::ClientStats_Dump"
void CDirectVoiceClientEngine::ClientStats_Dump()
{
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "STATS DUMP: ----------------------------------------------------[Begin] " );
ClientStats_Dump_Record();
ClientStats_Dump_Playback();
ClientStats_Dump_Receive();
ClientStats_Dump_Transmit();
DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "STATS DUMP: ------------------------------------------------------[End] " );
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::ClientStats_Begin"
void CDirectVoiceClientEngine::ClientStats_Begin()
{
m_pStatsBlob->m_dwTimeStart = GetTickCount();
m_pStatsBlob->m_dwMaxBuffers = 1;
m_pStatsBlob->m_dwTotalBuffers = 1;
m_pStatsBlob->m_dwPDTMin = 0xFFFFFFFF;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::ClientStats_End"
void CDirectVoiceClientEngine::ClientStats_End()
{
m_pStatsBlob->m_dwTimeStop = GetTickCount();
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::ClientBufferAlloc"
PVOID CDirectVoiceClientEngine::ClientBufferAlloc( void *const pv, const DWORD dwSize )
{
return new BYTE[dwSize];
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::ClientBufferFree"
void CDirectVoiceClientEngine::ClientBufferFree( void *const pv, void *const pvBuffer )
{
delete pvBuffer;
}
#undef DPF_MODNAME
/*#define DPF_MODNAME "CDirectVoiceClientEngine::GetTransmitBuffer"
PDVTRANSPORT_BUFFERDESC CDirectVoiceClientEngine::GetTransmitBuffer( DWORD dwSize, LPVOID *ppvSendContext )
{
PDVTRANSPORT_BUFFERDESC pNewBuffer;
pNewBuffer = new DVTRANSPORT_BUFFERDESC;
pNewBuffer->pBufferData = new BYTE[dwSize];
pNewBuffer->dwBufferSize = dwSize;
pNewBuffer->lRefCount = 0;
pNewBuffer->dwObjectType = DVTRANSPORT_OBJECTTYPE_CLIENT;
pNewBuffer->dwFlags = 0;
*ppvSendContext = pNewBuffer;
return pNewBuffer;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::ReturnTransmitBuffer"
// ReturnTransmitBuffer
//
// PDVTRANSPORT_BUFFERDESC pBufferDesc - Buffer description of buffer to return
// LPVOID lpvContext - Context value to be used when returning the buffer
//
void CDirectVoiceClientEngine::ReturnTransmitBuffer( PVOID pvContext )
{
PDVTRANSPORT_BUFFERDESC pBufferDesc = (PDVTRANSPORT_BUFFERDESC) pvContext;
delete [] pBufferDesc->pBufferData;
delete pBufferDesc;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::SendComplete"
HRESULT CDirectVoiceClientEngine::SendComplete( PDVEVENTMSG_SENDCOMPLETE pSendComplete )
{
ReturnTransmitBuffer( pSendComplete->pvUserContext );
return DV_OK;
}*/
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::GetTransmitBuffer"
PDVTRANSPORT_BUFFERDESC CDirectVoiceClientEngine::GetTransmitBuffer( DWORD dwSize, LPVOID *ppvSendContext )
{
PDVTRANSPORT_BUFFERDESC pNewBuffer = NULL;
DWORD dwFPMIndex = 0xFFFFFFFF;
DWORD dwWastedSpace = 0xFFFFFFFF;
DWORD dwSearchFPMIndex = 0;
DNEnterCriticalSection( &m_csTransmitBufferLock );
pNewBuffer = (PDVTRANSPORT_BUFFERDESC) m_BufferDescPool.Get( );
DNLeaveCriticalSection( &m_csTransmitBufferLock );
DPFX(DPFPREP, DVF_BUFFERDESC_DEBUG_LEVEL, "BUFFERDESC: Got a buffer desc address 0x%p", (void *) pNewBuffer );
if( pNewBuffer == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error getting transmit buffer" );
goto GETTRANSMITBUFFER_ERROR;
}
pNewBuffer->lRefCount = 0;
pNewBuffer->dwObjectType = DVTRANSPORT_OBJECTTYPE_CLIENT;
pNewBuffer->dwFlags = 0;
pNewBuffer->pBufferData = NULL;
for( dwSearchFPMIndex = 0; dwSearchFPMIndex < m_dwNumPools; dwSearchFPMIndex++ )
{
// Potential pool
if( m_pdwBufferPoolSizes[dwSearchFPMIndex] >= dwSize )
{
if( m_pdwBufferPoolSizes[dwSearchFPMIndex] - dwSize < dwWastedSpace )
{
dwWastedSpace = m_pdwBufferPoolSizes[dwSearchFPMIndex] - dwSize;
dwFPMIndex = dwSearchFPMIndex;
}
}
}
if( dwFPMIndex == 0xFFFFFFFF )
{
DNASSERT( FALSE );
DPFX(DPFPREP, 0, "Could not find pool large enough for buffer" );
goto GETTRANSMITBUFFER_ERROR;
}
pNewBuffer->pvContext = &m_pBufferPools[dwFPMIndex];
DNEnterCriticalSection( &m_csTransmitBufferLock );
pNewBuffer->pBufferData = (PBYTE) m_pBufferPools[dwFPMIndex].Get();
DNLeaveCriticalSection( &m_csTransmitBufferLock );
DPFX(DPFPREP, DVF_BUFFERDESC_DEBUG_LEVEL, "BUFFERDESC: Got a buffer value at address 0x%p", (void *) pNewBuffer->pBufferData );
DPFX(DPFPREP, DVF_BUFFERDESC_DEBUG_LEVEL, "BUFFERDESC: nInUse = %i", m_BufferDescPool.GetInUseCount() );
if( pNewBuffer->pBufferData == NULL )
{
DPFX(DPFPREP, 0, "Error getting buffer for buffer desc" );
goto GETTRANSMITBUFFER_ERROR;
}
pNewBuffer->dwBufferSize = dwSize;
*ppvSendContext = pNewBuffer;
return pNewBuffer;
GETTRANSMITBUFFER_ERROR:
DNEnterCriticalSection( &m_csTransmitBufferLock );
if( pNewBuffer != NULL && pNewBuffer->pBufferData != NULL )
{
((CFixedPool*) pNewBuffer->pvContext)->Release( pNewBuffer->pBufferData );
}
if( pNewBuffer != NULL )
{
m_BufferDescPool.Release( pNewBuffer );
}
DNLeaveCriticalSection( &m_csTransmitBufferLock );
return NULL;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::ReturnTransmitBuffer"
// ReturnTransmitBuffer
//
// PDVTRANSPORT_BUFFERDESC pBufferDesc - Buffer description of buffer to return
// LPVOID lpvContext - Context value to be used when returning the buffer
//
void CDirectVoiceClientEngine::ReturnTransmitBuffer( PVOID pvContext )
{
PDVTRANSPORT_BUFFERDESC pBufferDesc = (PDVTRANSPORT_BUFFERDESC) pvContext;
CFixedPool* pPool = (CFixedPool*) pBufferDesc->pvContext;
DPFX(DPFPREP, DVF_BUFFERDESC_DEBUG_LEVEL, "BUFFERDESC: Returning a buffer desc at address 0x%p", (void *) pBufferDesc );
DPFX(DPFPREP, DVF_BUFFERDESC_DEBUG_LEVEL, "BUFFERDESC: Returning a buffer at address 0x%p", (void *) pBufferDesc->pBufferData );
DNEnterCriticalSection( &m_csTransmitBufferLock );
// Release memory
pPool->Release( pBufferDesc->pBufferData );
// Release buffer description
m_BufferDescPool.Release( pvContext );
DPFX(DPFPREP, DVF_BUFFERDESC_DEBUG_LEVEL, "BUFFERDESC: nInUse = %i", m_BufferDescPool.GetInUseCount() );
DNLeaveCriticalSection( &m_csTransmitBufferLock );
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::SendComplete"
HRESULT CDirectVoiceClientEngine::SendComplete( PDVEVENTMSG_SENDCOMPLETE pSendComplete )
{
ReturnTransmitBuffer( pSendComplete->pvUserContext );
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::SetupInitialBuffers"
// SetupBuffersInitial
//
// This function sets up the first transmit buffers which do not vary
// in size w/the compression type.
//
HRESULT CDirectVoiceClientEngine::SetupInitialBuffers()
{
HRESULT hr = DV_OK;
DWORD dwIndex;
m_dwNumPools = CLIENT_POOLS_NUM;
if (!m_BufferDescPool.Initialize( sizeof(DVTRANSPORT_BUFFERDESC), NULL, NULL, NULL, NULL ))
{
DPFX(DPFPREP, 0, "Error allocating memory" );
hr = DVERR_OUTOFMEMORY;
goto SETUPBUFFERS_ERROR;
}
m_pBufferPools = new CFixedPool[m_dwNumPools];
if( m_pBufferPools == NULL )
{
DPFX(DPFPREP, 0, "Error allocating memory" );
hr = DVERR_OUTOFMEMORY;
goto SETUPBUFFERS_ERROR;
}
m_pdwBufferPoolSizes = new DWORD[m_dwNumPools];
if( m_pdwBufferPoolSizes == NULL )
{
DPFX(DPFPREP, 0, "Error allocating memory" );
hr = DVERR_OUTOFMEMORY;
goto SETUPBUFFERS_ERROR;
}
//This is broken if the number of client pools is ever adjusted from default
//value of 3. Since that's an unlikely change just compile time assert for now
DBG_CASSERT(CLIENT_POOLS_NUM==3);
m_pdwBufferPoolSizes[0] = CLIENT_POOLS_SIZE_MESSAGE;
m_pdwBufferPoolSizes[1] = CLIENT_POOLS_SIZE_PLAYERLIST;
m_pdwBufferPoolSizes[ m_dwNumPools-1] = 0;
for( dwIndex = 0; dwIndex < m_dwNumPools-1; dwIndex++ )
{
if (!m_pBufferPools[dwIndex].Initialize( m_pdwBufferPoolSizes[dwIndex], NULL, NULL, NULL, NULL ))
{
DPFX(DPFPREP, 0, "Error creating transmit buffers" );
hr=DVERR_OUTOFMEMORY;
goto SETUPBUFFERS_ERROR;
}
}
return DV_OK;
SETUPBUFFERS_ERROR:
FreeBuffers();
return hr;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceServerEngine::SetupSpeechBuffer"
// SetupSpeechBuffer
//
// This function sets up the buffer pool for speech sends, whose size will
// depend on the compression type. Must be done after we know CT but
// before we do first speech transmission.
//
HRESULT CDirectVoiceClientEngine::SetupSpeechBuffer()
{
if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_PEER ||
m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_ECHO )
{
m_pdwBufferPoolSizes[m_dwNumPools-1] = sizeof( DVPROTOCOLMSG_SPEECHHEADER )+m_dwCompressedFrameSize+COMPRESSION_SLUSH;
}
else
{
m_pdwBufferPoolSizes[m_dwNumPools-1] = sizeof( DVPROTOCOLMSG_SPEECHWITHTARGET ) + m_dwCompressedFrameSize +
(sizeof( DVID )*CLIENT_POOLS_NUM_TARGETS_BUFFERED)+COMPRESSION_SLUSH;
}
if (!m_pBufferPools[m_dwNumPools-1].Initialize( m_pdwBufferPoolSizes[m_dwNumPools-1], NULL, NULL, NULL, NULL ))
{
DPFX(DPFPREP, 0, "Error creating transmit buffers" );
return DVERR_OUTOFMEMORY;
}
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceServerEngine::FreeBuffers"
HRESULT CDirectVoiceClientEngine::FreeBuffers()
{
DWORD dwIndex;
if( m_pBufferPools != NULL )
{
for( dwIndex = 0; dwIndex < m_dwNumPools; dwIndex++ )
{
if (m_pdwBufferPoolSizes[dwIndex])
m_pBufferPools[dwIndex].DeInitialize( );
}
delete [] m_pBufferPools;
m_pBufferPools = NULL;
}
if( m_pdwBufferPoolSizes != NULL )
{
delete [] m_pdwBufferPoolSizes;
m_pdwBufferPoolSizes = 0;
}
m_BufferDescPool.DeInitialize( );
m_dwNumPools = 0;
return DV_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::ValidateSessionType"
BOOL CDirectVoiceClientEngine::ValidateSessionType( DWORD dwSessionType )
{
return (dwSessionType > 0 && dwSessionType < DVSESSIONTYPE_MAX);
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::ValidateSessionFlags"
BOOL CDirectVoiceClientEngine::ValidateSessionFlags( DWORD dwFlags )
{
return (dwFlags < DVSESSION_MAX);
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::ValidatePlayerFlags"
// Only allow player flags that specify either a 0 or the half duplex flags
BOOL CDirectVoiceClientEngine::ValidatePlayerFlags(DWORD dwFlags)
{
return (dwFlags == DVPLAYERCAPS_HALFDUPLEX || dwFlags == 0);
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::ValidatePlayerDVID"
BOOL CDirectVoiceClientEngine::ValidatePlayerDVID(DVID dvid)
{
return (dvid != DVID_ALLPLAYERS);
}
#undef DPF_MODNAME
#define DPF_MODNAME "CDirectVoiceClientEngine::ValidatePacketType"
BOOL CDirectVoiceClientEngine::ValidatePacketType( const DVPROTOCOLMSG_FULLMESSAGE* lpdvFullMessage ) const
{
switch( lpdvFullMessage->dvGeneric.dwType )
{
case DVMSGID_HOSTMIGRATELEAVE:
case DVMSGID_HOSTMIGRATED:
case DVMSGID_CREATEVOICEPLAYER:
case DVMSGID_DELETEVOICEPLAYER:
case DVMSGID_PLAYERLIST:
return ( ( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_PEER ) );
break;
case DVMSGID_SPEECHWITHFROM:
return ( ( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_FORWARDING) );
break;
case DVMSGID_SETTARGETS:
return ( m_dvSessionDesc.dwFlags & DVSESSION_SERVERCONTROLTARGET );
break;
}
return TRUE;
}