/*========================================================================== * * 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(lpMessage), dwSize ); break; case DVMSGID_HOSTMIGRATED: fResult = HandleHostMigrated( dvidSource, static_cast(lpMessage),dwSize ); break; case DVMSGID_CONNECTREFUSE: fResult = HandleConnectRefuse( dvidSource, static_cast(lpMessage), dwSize ); break; case DVMSGID_CONNECTACCEPT: fResult = HandleConnectAccept( dvidSource, static_cast(lpMessage), dwSize ); break; case DVMSGID_CREATEVOICEPLAYER: fResult = HandleCreateVoicePlayer( dvidSource, static_cast(lpMessage), dwSize ); break; case DVMSGID_DELETEVOICEPLAYER: fResult = HandleDeleteVoicePlayer( dvidSource, static_cast(lpMessage), dwSize ); break; case DVMSGID_SPEECH: fResult = HandleSpeech( dvidSource, static_cast(lpMessage), dwSize ); break; case DVMSGID_SPEECHBOUNCE: fResult = HandleSpeechBounce( dvidSource, static_cast(lpMessage), dwSize ); break; case DVMSGID_SPEECHWITHFROM: fResult = HandleSpeechWithFrom( dvidSource, static_cast(lpMessage), dwSize ); break; case DVMSGID_DISCONNECTCONFIRM: fResult = HandleDisconnectConfirm( dvidSource, static_cast(lpMessage), dwSize); break; case DVMSGID_SETTARGETS: fResult = HandleSetTarget( dvidSource, static_cast(lpMessage), dwSize ); break; case DVMSGID_SESSIONLOST: fResult = HandleSessionLost( dvidSource, static_cast(lpMessage), dwSize ); break; case DVMSGID_PLAYERLIST: fResult = HandlePlayerList( dvidSource, static_cast(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(_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; }