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

7999 lines
243 KiB

  1. /*==========================================================================
  2. *
  3. * Copyright (C) 1999 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: dvclientengine.cpp
  6. * Content: Implementation of class for DirectXVoice Client
  7. *
  8. * History:
  9. * Date By Reason
  10. * ==== == ======
  11. * 07/19/99 rodtoll Created
  12. * 07/21/99 rodtoll Added settings confirm message to protocol
  13. * Added storing user flags
  14. * 07/22/99 rodtoll Updated to use new player class
  15. * Improved concurrency protection
  16. * Added client/server support
  17. * 07/23/99 rodtoll Added multicast support
  18. * Modified notify loop, now checks for multicast timeouts
  19. * Modified playback loop to kill timed-out users
  20. * Minor fixes to client/server support
  21. * Other bugfixes.
  22. * Removed valid target check in multicast and client/server
  23. * 07/26/99 rodtoll Updated to support IDirectXVoiceNotify interface
  24. * 07/29/99 pnewson Updated call to CInputQueue2 constructor
  25. * 07/29/99 rodtoll Removed warnings, updated for new queue, added better
  26. * parameter checking.
  27. * 07/30/99 rodtoll Updated InitializeSound to use the parameters passed
  28. * in through the sounddeviceconfig.
  29. * 08/02/99 rodtoll Fixed bug in record volume
  30. * 08/03/99 pnewson Cleanup of Frame and Queue classes
  31. * 08/03/99 rodtoll Fixed calls into the transport
  32. * 08/04/99 rodtoll Fixed up Get/SetClientConfig
  33. * Added pointer to SoundConfig for second param of connect result
  34. * Half duplex clients won't get record level notifications anymore
  35. * Added connect result when error on connect
  36. * Modified so that soundeviceconfig gets ptr to dsound/dsc devices
  37. * Fixed bug w/sensitivity setting
  38. * 08/05/99 rodtoll Fixed locking, was causing deadlock w/DirectPlay
  39. * 08/10/99 rodtoll Initial support for host migration
  40. * rodtoll No longer creates a queue for ourselves
  41. * 08/18/99 rodtoll Fixed bug w/multicast. Added support for SPEECHBOUNCE
  42. * message type.
  43. * 08/25/99 rodtoll General Cleanup/Modifications to support new
  44. * compression sub-system.
  45. * 08/26/99 rodtoll Added copy of flags when doing SetClientConfig
  46. * rodtoll Fixed playback thread to properly handle playback mute
  47. * 08/27/99 rodtoll Fixed record start/stop notifications target info
  48. * rodtoll Added playervoicestart and playervoice stop messages
  49. * 08/30/99 rodtoll Fixed disconnect when sound init fails
  50. * Fixed disconnect when compression type unsupported
  51. * Fixed notification intervals
  52. * Added timeouts to connect and disconnect processes
  53. * Fixed bug in disconnect code
  54. * Added re-transmission of connect request
  55. * Fixed host migration notifications
  56. * Fixed return code on GetCompressionTypes call.
  57. * Updated to use new format specifications on playback
  58. * 08/31/99 rodtoll Logic re-write to fix shutdown problems.
  59. * - Notify thread now starts as soon as Initialize called
  60. * and stops when object destroyed
  61. * - Disconnect based on Disconnectconfirm or loss of
  62. * connection signals event, notify thread then handles
  63. * - Updated playback format to use 8Khz for playback
  64. * 09/01/99 rodtoll Added check for valid pointers in func calls
  65. * 09/02/99 rodtoll Added checks to handle case no local player created
  66. * Re-activated and fixed old auto record volume code
  67. * 09/03/99 rodtoll Re-work of playback core to support mixing to multiple buffers
  68. * for 3d support.
  69. * Re-worked playback to use position instead of notifications
  70. * allows for simpler handling of high CPU and 3d support
  71. * Implemented CreateUserBuffer/DeleteUserBuffer
  72. * 09/04/99 rodtoll Added code to delete 3d buffers for users/groups when they
  73. * are destroyed.
  74. * 09/07/99 rodtoll Added support for server controlled target message
  75. * rodtoll Added support for set/get user buffer for notarget
  76. * rodtoll Fixed InitHalfDuplex call -- was defaulting to default device always
  77. * 09/07/99 rodtoll Placed some guards to fix crash when Releasing when not initialized
  78. * 09/07/99 rodtoll Updated to allow buffer management on buffer for remaining users (main buffer)
  79. * rodtoll Updated return codes to use new errors (3d related)
  80. * 09/08/99 rodtoll Fixed playback level checking.
  81. * 09/10/99 rodtoll Implemented the DVCLIENTCONFIG_MUTEGLOBAL flag
  82. * rodtoll Additional parameter validations
  83. * 09/13/99 rodtoll Added preliminary support for new DVSOUNDDEVICECONFIG structure
  84. * 09/14/99 rodtoll Added new DVMSGID_PLAYEROUTPUTLEVEL message
  85. * rodtoll Added new SetNotifyMask function
  86. * rodtoll Updated INiitalize parameters to take notification masks.
  87. * rodtoll Implemented notification masks. (Allows user to choose which notifications to receive)
  88. * rodtoll Added CheckShouldSendMessage
  89. * rodtoll Added SendPlayerLevels (DVMSGID_PLAYEROUTPUTLEVEL messages handler)
  90. * 09/15/99 rodtoll Added DVMSGID_SETTARGET message when target is set by remote server
  91. * 09/16/99 rodtoll Updated Disconnect to alllow async and sync abortions of connects
  92. * 09/17/99 rodtoll Fixed bug in recordthread in error handling
  93. * 09/18/99 rodtoll Added HandleThreadError to be called when an internal thread dies.
  94. * 09/20/99 rodtoll Updated to return SESSIONLOST message instead of DISCONNECT when session is lost
  95. * rodtoll Functions will return DVERR_SESSIONLOST if called after session is lost.
  96. * rodtoll Improved error checking in playback thread
  97. * rodtoll Added more checks for memory alloc failures
  98. * rodtoll Small bugfix in playeroutputlevel messages
  99. * rodtoll Stricter checks on valid notify arrays
  100. * rodtoll Added proper error checking to SetNotifyMask
  101. * 09/27/99 rodtoll Fixed playback volume control
  102. * rodtoll Fixed bug w/echo servers w/clients running on same dvid as host
  103. * 09/28/99 rodtoll Fixed playervoicestart/stop notifications, now send source in the dwFrom param.
  104. * rodtoll Double notifications of local client when host migrates fixed.
  105. * rodtoll Added queue for notifications, notifications are added to the queue and
  106. * then signalled by the notify thread. (Prevents problems caused by notify
  107. * handlers taking a long time to return).
  108. * rodtoll Added voice suppression
  109. * 09/29/99 pnewson Major AGC overhaul
  110. * 10/04/99 rodtoll Added usage of the DVPROTOCOL_VERSION_XXX macros
  111. * rodtoll Added comments
  112. * rodtoll Fixed crash which occurs if object released before initialized
  113. * 10/05/99 rodtoll Added guards to DoDisconnect. If recording locked up on shutdown, then
  114. * DoDisconnect would be called twice --> Crash! Fixed.
  115. * rodtoll Reversed order of recording/playback shutdown. Shutting down playback
  116. * before recording caused recording lockup on ESS cards.
  117. * rodtoll Additional documentation
  118. * 10/07/99 rodtoll Updated to work in Unicode
  119. * rodtoll Modified notifications so connectresult should always be first
  120. * Removed release of write locks so that connect result would be queued first.
  121. * 10/15/99 pnewson Added config check in Connect call
  122. * 10/18/99 rodtoll Fix: Calling initialize twice doesn't fail.
  123. * 10/19/99 rodtoll Fix: Bug #113904 Shutdown Issues
  124. * - Added handler for SESSIONLOST messages. Fixes shutdown lockup.
  125. * - Changed disconnectAck event to manual reset so multiple threads can wait
  126. * on it. Neccessary to ensure disconnect is completed before release is done
  127. * - Changed behaviour of disconnect so that if you specify SYNC and disconnect
  128. * is in progress, you wait for complete. Required to support disconnect in releae
  129. * 10/25/99 rodtoll Fix: Bug #114684 - GetCaps causes lockup on shutdown
  130. * rodtoll Fix: Bug #114223 - Debug messages being printed at error level when inappropriate
  131. * 10/27/99 pnewson Fix: Bug #113935 - Saved AGC values should be device specific
  132. * Fix: Bug #113936 - Wizard should reset the AGC level before loopback test
  133. * Note: this fix adds the DVCLIENTCONFIG_AUTOVOLUMERESET flag
  134. * 10/28/99 pnewson Bug #114176 updated DVSOUNDDEVICECONFIG struct
  135. * 10/29/99 rodtoll Bug #113726 - Integrate Voxware Codecs, updating to use new
  136. * pluggable codec architecture. In order to support new architecture
  137. * all codecs creates were moved to threads where CoInitialize has been called.
  138. * rodtoll Fixed memory leak in multicast mode caused by new architecture
  139. * 11/04/99 pnewson Bug #114297 - Added HWND to SupervisorCheckAudioSetup call
  140. * 11/12/99 rodtoll Updated to use new playback and record classes and remove
  141. * old playback/record system (Includes waveIN/waveOut support)
  142. * rodtoll Updated to support new recording thread
  143. * rodtoll Added new echo suppression code
  144. * 11/16/99 rodtoll Recording thread now loops everytime it wakes up until it
  145. * has compressed and transmitted all the data it can before
  146. * going back to sleep.
  147. * 11/17/99 rodtoll Fix: Bug #115538 - dwSize members of > sizeof struct were accepted
  148. * rodtoll Fix: Bug #115827 - Calling SetNotifyMask w/no callback should fail
  149. * rodtoll Fix: Bug #117442 - Calling Disconnect with invalid flags doesn't return DVERR_INVALIDFLAGS
  150. * rodtoll Fix: Bug #117447 - GetTransmitTarget has problems
  151. * rodtoll Fix: Bug #117177 - Calling Connect w/o voice session never returns
  152. * 11/18/99 rodtoll Updated to control echo cancellation switching code by define.
  153. * 11/22/99 rodtoll Fixed problem caused by switching on echo cancellation while talking
  154. * 11/22/99 rodtoll Fixed Initialize() would fail incorrectly
  155. * 11/23/99 rodtoll Updated Initialize/SetNotifyMask so error checking behaviour is consistant
  156. * 11/24/99 rodtoll Adjusted Set/GetTransmit Target so locks are released before calling into dplay
  157. * 11/30/99 pnewson Reworked default device mapping code
  158. * Adjusted some timing issues to make single stepping connect possible
  159. * 12/01/99 rodtoll Bug #121815 - Recording/playback may contain static. Updated to call functions
  160. * to set conversion quality setting to high.
  161. * rodtoll Bug #115783 - Always adjusts volume for default device. Fixed for Win2k, Win9x w/DX7
  162. * Systems w/DX5 or none will use waveIN/waveOUT and will default to default device.
  163. * 12/02/99 rodtoll Bug #115783 - Will now use waveIN/waveOut object corresponding to specified GUID
  164. * on DX3 systems.
  165. * 12/06/99 rodtoll Bumped playback/record threads to time critical priority
  166. * 12/16/99 rodtoll Bug #117405 - 3D Sound APIs misleading - 3d sound apis renamed
  167. * The Delete3DSoundBuffer was re-worked to match the create
  168. * rodtoll Bug #122629 - Host migration broken in unusual configurations
  169. * Implemented new host migration scheme.
  170. * rodtoll Bug #121054 - DirectX 7.1 changes must be incorporated
  171. * rodtoll Implemented new DVPROTOCOLMSG_PLAYERLIST message to handle player table message.
  172. * rodtoll As part of new host migration, implemented proper handling of connection
  173. * rejected message (was broken, exposed by new host migration).
  174. * rodtoll Updated Disconnect to handle inability to contact server properly which
  175. * was resulting in an error message (when it should disconnect anyhow).
  176. * rodtoll Removed voice suppression
  177. * 01/10/00 pnewson AGC and VA tuning
  178. * 01/14/2000 rodtoll Updated for new speech packet types / packet handling
  179. * rodtoll Updated for new Get/SetTransmitTargets functions
  180. * rodtoll Added support for multiple targets
  181. * rodtoll Added use of fixed pool manager to manage memory for
  182. * notifications.
  183. * rodtoll Updated notifications to support messages with memory.
  184. * rodtoll Updated message handler calls to use new format
  185. * rodtoll Updated all notifications to use new message structures
  186. * rodtoll Updated Connect/Disconnect so that when DVFLAGS_SYNCH is
  187. * specified no completion messages will be sent.
  188. * rodtoll Added new API call GetSoundDeviceConfig
  189. * 01/21/2000 pnewson Changes in support of revised wizard UI
  190. * Allow concurrent AGC and user controlled volume
  191. * 01/24/2000 rodtoll Bug #129427: Calling Destroy3DSoundBuffer for player who has
  192. * already disconnected resulted in an incorrect DVERR_NOTBUFFERED
  193. * error code.
  194. * 01/27/2000 rodtoll Bug #129934 - Update Create3DSoundBuffer to take DSBUFFERDESC
  195. * 01/28/2000 rodtoll Bug #130465: Record Mute/Unmute must call YieldFocus() / ClaimFocus()
  196. * 02/01/2000 rodtoll Disable capture focus features - Bug #129457
  197. * 02/08/2000 rodtoll Bug #131496 - Selecting DVTHRESHOLD_DEFAULT results in voice
  198. * never being detected
  199. * 02/15/2000 rodtoll Fixed Connect so mapping GUIDs doesn't stomp user structure
  200. * 02/17/2000 rodtoll Bug #133691 - Choppy audio - queue was not adapting
  201. * Added instrumentation
  202. * 03/28/2000 rodtoll Re-wrote nametable handling and locking -- more scalable
  203. * rodtoll Fixed pool for players
  204. * rodtoll Bilink of "active players" and "players to notify" allows for greater
  205. * concurrency (playback and notify threads don't need to lock entire
  206. * nametable while running.
  207. * 03/29/2000 rodtoll Bug #30957 - Made conversion quality slider setting optional -- new flag -- DVSOUNDCONFIG_SETCONVERSIONQUALITY
  208. * rodtoll Incorporated experimental playback handling w/lower priority and more frequent wakeup
  209. * rodtoll Instead of calling ConfirmValidEntity now checks nametable
  210. * 04/07/2000 rodtoll Bug #32179 - Prevent registration of > 1 interface
  211. * rodtoll Updated to use no copy sends, so handles pooling frames to be sent, proper
  212. * pulling of frames from pools and returns.
  213. * 04/19/2000 rodtoll Re-enabled capture focus behaviour / found not working on WDM, re-disabled.
  214. * rodtoll Bug #31106 - Handle sound devices w/no recording volume
  215. * Set DVSOUNDCONFIG_NORECVOLAVAILABLE flag on DVSOUNDDEVICECONFIG and do not
  216. * perform any volume sets when this flag is present
  217. * 04/20/2000 rodtoll Bug #31478 - Lockup in shutdown on client who has become new host -- ref count issue
  218. * 04/24/2000 rodtoll Bug #33228 - Compile error reported by davidkl
  219. * 04/27/2000 rodtoll Fix for host migration crash turned out to be sample bug, restoring.
  220. * rodtoll Fix for crash on Connect failed
  221. * 05/11/2000 rodtoll Bug #34852 Voice connection crashes after being in a session for a couple of minutes
  222. * 05/17/2000 rodtoll Bug #35110 Simultaneous playback of 2 voices results in distorted playback
  223. * 05/30/2000 rodtoll Bug #35476 Access violation in DPVOICE.DLL on host after client left
  224. * Host migration code was being fired because of DirectPlay8's nametable unwinding funcs.
  225. * 05/31/2000 rodtoll Bug #35860 - Fix VC6 compile errors for instrumented builds
  226. * rodtoll Bug #35794 - Setting targets to none results in leak.
  227. * 06/02/2000 rodtoll Moved host migration so it is keyed off voice message and transport messages.
  228. * More reliable this way.
  229. * 06/21/2000 rodtoll Bug #35767 - Implement ability for Dsound effects processing if dpvoice buffers
  230. * Updated Connect and Create3DSoundBuffer to take buffers instead of descriptions
  231. * rodtoll Bug #36820 - Host migrates to wrong client when server is shut down before host's client disconnects
  232. * Caused because client attempts to register new server when there is one already
  233. * rodtoll Bug #37045 - Race conditions prevent acknowledgement of new host
  234. * Added send when new host is elected of settingsconfirm message
  235. * 06/27/2000 rodtoll Fixed window where outstanding sends being returned after we have deregistered
  236. * Voice now waits for all outstanding voice sends to complete before shutting down
  237. * rodtoll Added COM abstraction
  238. * 06/28/2000 rodtoll Prefix Bug #38022
  239. * 07/01/2000 rodtoll Bug #38280 - DVMSGID_DELETEVOICEPLAYER messages are being sent in non-peer to peer sessions
  240. * Nametable will now only unravel with messages if session is peer to peer.
  241. * rodtoll Bug #38316 - HOST MIGRATION - Player fails to get HOST_MIGRATED message
  242. * 07/09/2000 rodtoll Added signature bytes
  243. * 07/12/2000 rodtoll Bug #39117 - Access violation while running VoicePosition. Several issues:
  244. * - Allow Destroy3DBuffer during disconnect
  245. * - Move nametable cleanup to before freesoundbufferlist
  246. * - Fixed code so always remove from list on DeleteSoundTarget
  247. * - Removed unneeded logic
  248. * 07/12/2000 rodtoll Bug #31468 - Add diagnostic spew to logfile to show what is failing the HW Wizard
  249. * rodtoll Bug #32841 - Renable support for capture focus
  250. * 07/22/2000 rodtoll Bug #40284 - Initialize() and SetNotifyMask() should return invalidparam instead of invalidpointer
  251. * rodtoll Bug #40296, 38858 - Crashes due to shutdown race condition
  252. * Now ensures that all threads from transport have left and that
  253. * all notificatinos have been processed before shutdown is complete.
  254. * rodtoll Bug #39586 - Trap 14 in DPVVOX.DLL during session of voicegroup, adding guards for overwrites
  255. * 07/26/2000 rodtoll Bug #40676 - Forwarding server is broken
  256. * 07/31/2000 rodtoll Bug #40470 - SetClientConfig() with invalid flags returns INVALIDPARAM
  257. * 08/03/2000 rodtoll Bug #41457 - DPVOICE: need way to discover which specific dsound call failed when returning DVERR_SOUNDINITFAILURE
  258. * 08/08/2000 rodtoll Was missing a DNLeaveCriticalSection
  259. * 08/09/2000 rodtoll Bug #41936 - No voice session message instead of compression not supported
  260. * 08/21/2000 rodtoll Bug #41475 - DPVOICE: Lockup during shutdown when deleteplayer messages received
  261. * 08/22/2000 rodtoll Bug #43095 - DPVOICE: DVMSGID_GAINFOCUS and DVMSGID_LOSTFOCUS are not passing NULL message parameters
  262. * 08/28/2000 masonb Voice Merge: DNet FPOOLs use DNCRITICAL_SECTION, modified m_pBufferDescPool usage
  263. * 08/29/2000 rodtoll Bug #43668 - DPVOICE: Asserts when exiting DPVOICE session
  264. * 08/31/2000 rodtoll Bug #43804 - DVOICE: dwSensitivity structure member is confusing - should be dwThreshold
  265. * 08/31/2000 rodtoll Whistler Bug #171841 - Prefix Bug
  266. * 09/01/2000 masonb Modified PlaybackThread, RecordThread, and NotifyThread to call _endthread to clean up thread handles
  267. * 09/14/2000 rodtoll Bug #45001 - DVOICE: AV if client has targetted > 10 players
  268. * 09/26/2000 rodtoll Bug #45541 - DPVOICE: Client gets DVERR_TIMEOUT message when disconnecting
  269. * 09/28/2000 rodtoll Fix Again: Bug #45541 - DPVOICE: Client gets DVERR_TIMEOUT message when disconnecting (Server always confirms disconnect)
  270. * 10/05/2000 rodtoll Bug #46541 - DPVOICE: A/V linking to dpvoice.lib could cause application to fail init and crash
  271. * 11/16/2000 rodtoll Bug #40587 - DPVOICE: Mixing server needs to use multi-processors
  272. * 01/22/2001 rodtoll WINBUG #288437 - IA64 Pointer misalignment due to wire packets
  273. * 01/25/2001 rodtoll WINBUG #293197 - DPVOICE: Stress applications cannot tell the difference between out of memory/invalid device/other errors
  274. * 01/26/2001 rodtoll WINBUG #293197 - DPVOICE: [STRESS} Stress applications cannot tell difference between out of memory and internal errors.
  275. * Remap DSERR_OUTOFMEMORY to DVERR_OUTOFMEMORY instead of DVERR_SOUNDINITFAILURE.
  276. * Remap DSERR_ALLOCATED to DVERR_PLAYBACKSYSTEMERROR instead of DVERR_SOUNDINITFAILURE.
  277. *
  278. * 04/04/2001 rodtoll WINBUG #343428 - DPVOICE: Voice wizard's playback is very choppy.
  279. * rodtoll WINBUG #356124 - STRESS: DPVLPY7 broke when Initialize() failed due to being out of memory.
  280. * 04/02/2001 simonpow Bug #354859 Fixes for problems spotted by PREfast (hresult casting to bool and local variable hiding)
  281. * 04/06/2001 kareemc Added Voice Defense
  282. * 04/12/2001 kareemc WINBUG #360971 - Wizard Memory Leaks
  283. * 04/09/2001 rodtoll WINBUG #363804 DPVOICE: Race condition in shutdown causes disconnect to timeout if session is being shutdown concurrently.
  284. * 04/11/2001 rodtoll WINBUG #221494 DPVOICE: Capped the # of queued recording events to prevent multiple wakeups resulting in false lockup
  285. * detection
  286. * 04/12/2001 simonpow WINBUG #322454 Removed early unlock in RemovePlayer method and added
  287. * extra return code checking in DoConnectResponse
  288. * 04/21/2001 rodtoll MANBUG #50058 DPVOICE: VoicePosition: No sound for couple of seconds when position bars are moved
  289. * - Added StartMix() call when secondary buffers are created
  290. * - Added extra bit of debug spew, removed dead code
  291. * 04/21/2001 rodtoll (for Simonpow)
  292. * WINBUG #322454 DPVOICE: [STRESS] Connect() appears to be suceeding (returns S_OK) but connection is not established.
  293. * 09/05/2001 simonpow Bug #463972. Added constuct/destruct methods to initialisation
  294. * calls on m_fpPlayers for CVoicePlayer
  295. * 11/12/2001 simonpow Bug #482302 Missing a DNLeaveCriticalSection in Cleanup
  296. * 02/25/2002 rodtoll WINBUG #550017 - SECURITY: DPVOICE: Client could inject speech into remove clients output
  297. * - Confirm voice messages in forwarding sessions come from the server.
  298. * rodtoll WINBUG #550009 - SECURITY: DPVOICE: Potential corruption of voice client state
  299. * - Harden receive pathways: i.e. confirm message sources from server, confirm message makes
  300. * sense w.r.t session settings, tighten validation, prevent duplicate disconnects from executing.
  301. * rodtoll WINBUG #550124 - SECURITY: DPVOICE: Shared memory region with NULL DACL
  302. * - Pull statistics exporting code
  303. * 03/01/2002 simonpow Fixed FreeBuffers to ensure it doesn't deinitialise any fixed
  304. * pools that haven't been initialised
  305. * 05/01/2002 simonpow Ensured GUID passed to DVCDB_GetCompressionInfo is aligned
  306. * in HandleConnectAccept
  307. * 06/13/2002 simonpow BUG #59944 Switched over to using Threadpool based timers rather than multimedia
  308. ***************************************************************************/
  309. #include "dxvoicepch.h"
  310. extern HRESULT DVS_Create(LPDIRECTVOICESERVEROBJECT *piDVS);
  311. // Use high priority for playback / record threads
  312. #define __CORE_THREAD_PRIORITY_HIGH
  313. // Disables the sound system
  314. //#define __DISABLE_SOUND
  315. // Forces full duplex mode
  316. //#define __FORCEFULLDUPLEX
  317. // # of ms of inactivity before a multicast user is considered to have
  318. // timed-out.
  319. #define DV_MULTICAST_USERTIMEOUT_PERIOD 300000
  320. // # of ms of inactivity before an incoming audio stream is considered to
  321. // have stopped. Used to determine when to send PLAYERVOICESTOP
  322. // message.
  323. #define PLAYBACK_RECEIVESTOP_TIMEOUT 500
  324. // # of ms the notify thread sleeps without notification to wakeup
  325. // and perform house cleaning.
  326. #define DV_CLIENT_NOTIFYWAKEUP_TIMEOUT 100
  327. // # of ms before a connect request is considered to have been lost
  328. #define DV_CLIENT_CONNECT_RETRY_TIMEOUT 1250
  329. // # of ms before we should timeout a connect request completely
  330. #define DV_CLIENT_CONNECT_TIMEOUT 30000
  331. // Maximum count notification semaphores can have
  332. #define DVCLIENT_NOTIFY_MAXSEMCOUNT 0x0FFFFFFF
  333. //// TODO: Needs tuning.
  334. // # of ms to wait for disconnect reply from server before timing out.
  335. #define DV_CLIENT_DISCONNECT_TIMEOUT 10000
  336. #define DV_CLIENT_SRCQUALITY_INVALID ((DIRECTSOUNDMIXER_SRCQUALITY) 0xFFFFFFFF)
  337. #define CLIENT_POOLS_NUM 3
  338. #define CLIENT_POOLS_SIZE_MESSAGE (sizeof(DVPROTOCOLMSG_FULLMESSAGE))
  339. #define CLIENT_POOLS_SIZE_PLAYERLIST DVPROTOCOL_PLAYERLIST_MAXSIZE
  340. // DV_CLIENT_EMULATED_LEAD_ADJUST
  341. //
  342. // # of buffer's worth of lead ahead of read pointer allowable to write
  343. // in playback buffer when buffer is emulated (additional, not total)
  344. #define DV_CLIENT_EMULATED_LEAD_ADJUST 2
  345. // DV_CLIENT_BASE_LEAD_MAX
  346. //
  347. // Maximum # of buffers lead to allow
  348. #define DV_CLIENT_BASE_LEAD_MAX 2
  349. // MixingWakeupProc
  350. //
  351. // This function is called by the DvTimer timer used by this
  352. // class each time the timer goes off. The function signals
  353. // a semaphore provided by the creator of the timer.
  354. //
  355. // Parameters:
  356. // DWORD param - A recast pointer to a HANDLE
  357. #undef DPF_MODNAME
  358. #define DPF_MODNAME "CDirectVoiceClientEngine::MixingWakeupProc"
  359. void CDirectVoiceClientEngine::MixingWakeupProc( void * pvUserData )
  360. {
  361. TimerHandlerParam *pParam = (TimerHandlerParam *) pvUserData;
  362. SetEvent( pParam->hPlaybackTimerEvent );
  363. SetEvent( pParam->hRecordTimerEvent );
  364. DNEnterCriticalSection( &pParam->csPlayCount );
  365. pParam->lPlaybackCount++;
  366. DNLeaveCriticalSection( &pParam->csPlayCount );
  367. }
  368. #undef DPF_MODNAME
  369. #define DPF_MODNAME "CDirectVoiceClientEngine::CDirectVoiceClientEngine"
  370. //
  371. // Constructor
  372. //
  373. // Initializes object to uninitialized state. Must call Initialize succesfully before
  374. // the object can be used (except GetCompressionTypes which can be called at any time).
  375. //
  376. CDirectVoiceClientEngine::CDirectVoiceClientEngine( DIRECTVOICECLIENTOBJECT *lpObject
  377. ): m_dwSignature(VSIG_CLIENTENGINE),
  378. m_lpFramePool(NULL),
  379. m_lpObject(lpObject),
  380. m_lpSessionTransport(NULL),
  381. m_lpUserContext(NULL),
  382. m_dvidServer(0),
  383. m_pTimer(NULL),
  384. m_bLastPeak(0),
  385. m_bLastTransmitted(FALSE),
  386. m_bMsgNum(0),
  387. m_bSeqNum(0),
  388. m_dwActiveCount(0),
  389. m_dwLastConnectSent(0),
  390. m_audioPlaybackBuffer(NULL),
  391. m_audioRecordDevice(NULL),
  392. m_audioPlaybackDevice(NULL),
  393. m_audioRecordBuffer(NULL),
  394. m_hRecordDone(NULL),
  395. m_hRecordTerminate(NULL),
  396. m_hPlaybackDone(NULL),
  397. m_hPlaybackTerminate(NULL),
  398. m_hConnectAck(NULL),
  399. m_dwCurrentState(DVCSTATE_NOTINITIALIZED),
  400. m_hrConnectResult(DVERR_GENERIC),
  401. m_hrOriginalConnectResult(DVERR_GENERIC),
  402. m_hrDisconnectResult(DV_OK),
  403. m_hDisconnectAck(NULL),
  404. m_hNotifyDone( NULL ),
  405. m_hNotifyTerminate( NULL ),
  406. m_hNotifyChange( NULL ),
  407. m_bLastPlaybackPeak( 0 ),
  408. m_lpdvServerMigrated(NULL),
  409. m_hNotifyDisconnect(NULL),
  410. m_hNotifyConnect(NULL),
  411. m_pFramePool(NULL),
  412. m_lpstGeneralBuffer(NULL),
  413. m_lpstBufferList(NULL),
  414. m_lpdwMessageElements(NULL),
  415. m_dwNumMessageElements(0),
  416. m_fSessionLost(FALSE),
  417. m_fLocalPlayerNotify(FALSE),
  418. m_lpNotifyList(NULL),
  419. m_hNewNotifyElement(NULL),
  420. m_dwHostOrderID(DVPROTOCOL_HOSTORDER_INVALID),
  421. m_pdvidTargets(NULL),
  422. m_dwNumTargets(0),
  423. m_dwTargetVersion(0),
  424. m_fConnectAsync(false),
  425. m_fDisconnectAsync(false),
  426. m_dwOriginalRecordQuality(DV_CLIENT_SRCQUALITY_INVALID),
  427. m_dwOriginalPlayQuality(DV_CLIENT_SRCQUALITY_INVALID),
  428. m_dwPlayActiveCount(0),
  429. m_fLocalPlayerAvailable(FALSE),
  430. m_fNotifyQueueEnabled(FALSE),
  431. m_hRecordThreadHandle(NULL),
  432. m_hPlaybackThreadHandle(NULL),
  433. m_dwMigrateHostOrderID(DVPROTOCOL_HOSTORDER_INVALID),
  434. m_pStatsBlob(NULL),
  435. m_fCritSecInited(FALSE),
  436. m_iPlayerListReceived(-1),
  437. m_fDisconnecting(FALSE)
  438. {
  439. m_dvSoundDeviceConfig.lpdsCaptureDevice = NULL;
  440. m_dvSoundDeviceConfig.lpdsPlaybackDevice = NULL;
  441. memset( &m_dvCaps, 0x00, sizeof( DVCAPS ) );
  442. m_dvCaps.dwSize = sizeof( DVCAPS );
  443. m_dvCaps.dwFlags = 0;
  444. ClientStats_Reset();
  445. }
  446. #undef DPF_MODNAME
  447. #define DPF_MODNAME "CDirectVoiceClientEngine::InitClass"
  448. BOOL CDirectVoiceClientEngine::InitClass( )
  449. {
  450. if (!DNInitializeCriticalSection( &m_csNotifyQueueLock ))
  451. {
  452. return FALSE;
  453. }
  454. if (!DNInitializeCriticalSection( &m_lockPlaybackMode ))
  455. {
  456. DNDeleteCriticalSection( &m_csNotifyQueueLock );
  457. return FALSE;
  458. }
  459. if (!DNInitializeCriticalSection( &m_csTargetLock ))
  460. {
  461. DNDeleteCriticalSection( &m_lockPlaybackMode );
  462. DNDeleteCriticalSection( &m_csNotifyQueueLock );
  463. return FALSE;
  464. }
  465. if (!DNInitializeCriticalSection( &m_csCleanupProtect ))
  466. {
  467. DNDeleteCriticalSection( &m_csTargetLock );
  468. DNDeleteCriticalSection( &m_lockPlaybackMode );
  469. DNDeleteCriticalSection( &m_csNotifyQueueLock );
  470. return FALSE;
  471. }
  472. if (!DNInitializeCriticalSection( &m_csBufferLock ))
  473. {
  474. DNDeleteCriticalSection( &m_csCleanupProtect );
  475. DNDeleteCriticalSection( &m_csTargetLock );
  476. DNDeleteCriticalSection( &m_lockPlaybackMode );
  477. DNDeleteCriticalSection( &m_csNotifyQueueLock );
  478. return FALSE;
  479. }
  480. if (!DNInitializeCriticalSection( &m_csPlayAddList ))
  481. {
  482. DNDeleteCriticalSection( &m_csBufferLock );
  483. DNDeleteCriticalSection( &m_csCleanupProtect );
  484. DNDeleteCriticalSection( &m_csTargetLock );
  485. DNDeleteCriticalSection( &m_lockPlaybackMode );
  486. DNDeleteCriticalSection( &m_csNotifyQueueLock );
  487. return FALSE;
  488. }
  489. if (!DNInitializeCriticalSection( &m_csNotifyAddList ))
  490. {
  491. DNDeleteCriticalSection( &m_csPlayAddList );
  492. DNDeleteCriticalSection( &m_csBufferLock );
  493. DNDeleteCriticalSection( &m_csCleanupProtect );
  494. DNDeleteCriticalSection( &m_csTargetLock );
  495. DNDeleteCriticalSection( &m_lockPlaybackMode );
  496. DNDeleteCriticalSection( &m_csNotifyQueueLock );
  497. return FALSE;
  498. }
  499. if (!DNInitializeCriticalSection( &m_csTransmitBufferLock ))
  500. {
  501. DNDeleteCriticalSection( &m_csNotifyAddList );
  502. DNDeleteCriticalSection( &m_csPlayAddList );
  503. DNDeleteCriticalSection( &m_csBufferLock );
  504. DNDeleteCriticalSection( &m_csCleanupProtect );
  505. DNDeleteCriticalSection( &m_csTargetLock );
  506. DNDeleteCriticalSection( &m_lockPlaybackMode );
  507. DNDeleteCriticalSection( &m_csNotifyQueueLock );
  508. return FALSE;
  509. }
  510. if (!DNInitializeCriticalSection( &m_thTimerInfo.csPlayCount ))
  511. {
  512. DNDeleteCriticalSection( &m_csTransmitBufferLock );
  513. DNDeleteCriticalSection( &m_csNotifyAddList );
  514. DNDeleteCriticalSection( &m_csPlayAddList );
  515. DNDeleteCriticalSection( &m_csBufferLock );
  516. DNDeleteCriticalSection( &m_csCleanupProtect );
  517. DNDeleteCriticalSection( &m_csTargetLock );
  518. DNDeleteCriticalSection( &m_lockPlaybackMode );
  519. DNDeleteCriticalSection( &m_csNotifyQueueLock );
  520. return FALSE;
  521. }
  522. if (!DNInitializeCriticalSection( &m_csClassLock ))
  523. {
  524. DNDeleteCriticalSection( &m_thTimerInfo.csPlayCount );
  525. DNDeleteCriticalSection( &m_csTransmitBufferLock );
  526. DNDeleteCriticalSection( &m_csNotifyAddList );
  527. DNDeleteCriticalSection( &m_csPlayAddList );
  528. DNDeleteCriticalSection( &m_csBufferLock );
  529. DNDeleteCriticalSection( &m_csCleanupProtect );
  530. DNDeleteCriticalSection( &m_csTargetLock );
  531. DNDeleteCriticalSection( &m_lockPlaybackMode );
  532. DNDeleteCriticalSection( &m_csNotifyQueueLock );
  533. return FALSE;
  534. }
  535. if (!DNInitializeCriticalSection( &m_csNotifyLock ))
  536. {
  537. DNDeleteCriticalSection( &m_csClassLock );
  538. DNDeleteCriticalSection( &m_thTimerInfo.csPlayCount );
  539. DNDeleteCriticalSection( &m_csTransmitBufferLock );
  540. DNDeleteCriticalSection( &m_csNotifyAddList );
  541. DNDeleteCriticalSection( &m_csPlayAddList );
  542. DNDeleteCriticalSection( &m_csBufferLock );
  543. DNDeleteCriticalSection( &m_csCleanupProtect );
  544. DNDeleteCriticalSection( &m_csTargetLock );
  545. DNDeleteCriticalSection( &m_lockPlaybackMode );
  546. DNDeleteCriticalSection( &m_csNotifyQueueLock );
  547. return FALSE;
  548. }
  549. m_fCritSecInited = TRUE;
  550. return TRUE;
  551. }
  552. #undef DPF_MODNAME
  553. #define DPF_MODNAME "CDirectVoiceClientEngine::~CDirectVoiceClientEngine"
  554. // Destructor
  555. //
  556. // This function requires a write lock to complete.
  557. //
  558. // If the object is connected to a session, it will be disconnected
  559. // by this function.
  560. //
  561. // Releases the resources associated with the object and stops the
  562. // notifythread.
  563. //
  564. // Locks Required:
  565. // - Class Write Lock
  566. //
  567. // Called By:
  568. // DVC_Release when reference count reaches 0.
  569. //
  570. CDirectVoiceClientEngine::~CDirectVoiceClientEngine()
  571. {
  572. CDVCSLock guardLock(&m_csClassLock);
  573. guardLock.Lock();
  574. if( m_dwCurrentState != DVCSTATE_IDLE &&
  575. m_dwCurrentState != DVCSTATE_NOTINITIALIZED )
  576. {
  577. Cleanup();
  578. }
  579. if( m_hNotifyDone != NULL )
  580. {
  581. SetEvent( m_hNotifyTerminate );
  582. WaitForSingleObject( m_hNotifyDone, INFINITE );
  583. CloseHandle( m_hNotifyDone );
  584. CloseHandle( m_hNotifyTerminate );
  585. CloseHandle( m_hNotifyChange );
  586. CloseHandle( m_hNotifyDisconnect );
  587. CloseHandle( m_hNotifyConnect );
  588. m_hNotifyDone = NULL;
  589. m_hNotifyTerminate = NULL;
  590. m_hNotifyChange = NULL;
  591. m_hNotifyDisconnect = NULL;
  592. m_hNotifyConnect = NULL;
  593. }
  594. if( m_hConnectAck != NULL )
  595. CloseHandle( m_hConnectAck );
  596. if( m_hDisconnectAck != NULL )
  597. CloseHandle( m_hDisconnectAck );
  598. if( m_lpdvServerMigrated != NULL )
  599. {
  600. m_lpdvServerMigrated->Release();
  601. m_lpdvServerMigrated = NULL;
  602. }
  603. if( m_lpdwMessageElements != NULL )
  604. delete [] m_lpdwMessageElements;
  605. guardLock.Unlock();
  606. NotifyQueue_Free();
  607. if (m_fCritSecInited)
  608. {
  609. DNDeleteCriticalSection( &m_lockPlaybackMode );
  610. DNDeleteCriticalSection( &m_csTargetLock );
  611. DNDeleteCriticalSection( &m_csCleanupProtect );
  612. DNDeleteCriticalSection( &m_csBufferLock );
  613. DNDeleteCriticalSection( &m_thTimerInfo.csPlayCount );
  614. DNDeleteCriticalSection( &m_csPlayAddList );
  615. DNDeleteCriticalSection( &m_csNotifyAddList );
  616. DNDeleteCriticalSection( &m_csTransmitBufferLock );
  617. DNDeleteCriticalSection( &m_csClassLock );
  618. DNDeleteCriticalSection( &m_csNotifyQueueLock );
  619. DNDeleteCriticalSection( &m_csNotifyLock );
  620. }
  621. if( m_pdvidTargets != NULL )
  622. {
  623. delete [] m_pdvidTargets;
  624. }
  625. m_dwSignature = VSIG_CLIENTENGINE_FREE;
  626. }
  627. // InternalSetNotifyMask
  628. //
  629. // Sets the list of valid notifiers for this object.
  630. //
  631. // Locks Needed:
  632. // - ReadLock to check status and then releases it.
  633. // - m_csNotifyLock to update notification list
  634. //
  635. // Called By:
  636. // DVC_SetNotifyMask
  637. //
  638. #undef DPF_MODNAME
  639. #define DPF_MODNAME "CDirectVoiceClientEngine::InternalSetNotifyMask"
  640. HRESULT CDirectVoiceClientEngine::InternalSetNotifyMask( LPDWORD lpdwMessages, DWORD dwNumElements )
  641. {
  642. BFCSingleLock slLock( &m_csNotifyLock );
  643. slLock.Lock();
  644. // Delete previous elements
  645. if( m_lpdwMessageElements != NULL )
  646. {
  647. delete [] m_lpdwMessageElements;
  648. }
  649. m_dwNumMessageElements = dwNumElements;
  650. // Make copies of the message elements into our own message array.
  651. if( dwNumElements > 0 )
  652. {
  653. m_lpdwMessageElements = new DWORD[dwNumElements];
  654. if( m_lpdwMessageElements == NULL )
  655. {
  656. DPFX(DPFPREP, DVF_ERRORLEVEL, "Initialize: Error allocating memory" );
  657. return DVERR_OUTOFMEMORY;
  658. }
  659. memcpy( m_lpdwMessageElements, lpdwMessages, sizeof(DWORD)*dwNumElements );
  660. }
  661. else
  662. {
  663. m_lpdwMessageElements = NULL;
  664. }
  665. return DV_OK;
  666. }
  667. // SetNotifyMask
  668. //
  669. // Sets the list of valid notifiers for this object.
  670. //
  671. // Locks Needed:
  672. // - ReadLock to check status and then releases it.
  673. // - m_csNotifyLock to update notification list
  674. //
  675. // Called By:
  676. // DVC_SetNotifyMask
  677. //
  678. #undef DPF_MODNAME
  679. #define DPF_MODNAME "CDirectVoiceClientEngine::SetNotifyMask"
  680. HRESULT CDirectVoiceClientEngine::SetNotifyMask( LPDWORD lpdwMessages, DWORD dwNumElements )
  681. {
  682. HRESULT hr;
  683. DPFX(DPFPREP, DVF_ENTRYLEVEL, "Enter" );
  684. hr = DV_ValidMessageArray( lpdwMessages, dwNumElements, FALSE );
  685. if( FAILED( hr ) )
  686. {
  687. DPFX(DPFPREP, DVF_ERRORLEVEL, "ValidMessageArray Failed 0x%x", hr );
  688. return hr;
  689. }
  690. DPFX(DPFPREP, DVF_APIPARAM, "Message IDs=%d", dwNumElements );
  691. if( lpdwMessages != NULL )
  692. {
  693. for( DWORD dwIndex = 0; dwIndex < dwNumElements; dwIndex++ )
  694. {
  695. DPFX(DPFPREP, DVF_APIPARAM, "MessageIDs[%d] = %d", dwIndex, lpdwMessages[dwIndex] );
  696. }
  697. }
  698. CDVCSLock guardLock(&m_csClassLock);
  699. guardLock.Lock();
  700. if( m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
  701. {
  702. DPFX(DPFPREP, DVF_ERRORLEVEL, "Not initialized" );
  703. return DVERR_NOTINITIALIZED;
  704. }
  705. guardLock.Unlock();
  706. BFCSingleLock slLock( &m_csNotifyLock );
  707. slLock.Lock();
  708. if( m_lpMessageHandler == NULL )
  709. {
  710. DPFX(DPFPREP, DVF_ERRORLEVEL, "Cannot specify message mask there is no callback function" );
  711. return DVERR_NOCALLBACK;
  712. }
  713. hr = InternalSetNotifyMask( lpdwMessages, dwNumElements );
  714. DPFX(DPFPREP, DVF_APIPARAM, "Returning hr=0x%x", hr );
  715. return DV_OK;
  716. }
  717. // Initialize
  718. //
  719. // Initializes this object into a state where it can be used to Connect to a session. Sets the
  720. // notification function, notification mask and the transport object that will be used.
  721. //
  722. // Starts the notification thread.
  723. //
  724. // Locks Required:
  725. // - Class Write Lock
  726. //
  727. // Called By:
  728. // DV_Initialize
  729. //
  730. #undef DPF_MODNAME
  731. #define DPF_MODNAME "CDirectVoiceClientEngine::Initialize"
  732. HRESULT CDirectVoiceClientEngine::Initialize( CDirectVoiceTransport *lpTransport, LPDVMESSAGEHANDLER lpdvHandler, LPVOID lpUserContext, LPDWORD lpdwMessages, DWORD dwNumElements )
  733. {
  734. HRESULT hr;
  735. DPFX(DPFPREP, DVF_ENTRYLEVEL, "Enter" );
  736. // 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
  737. DPFX(DPFPREP, DVF_APIPARAM, "Param: lpTransport = 0x%p lpdvHandler = 0x%p lpUserContext = 0x%p dwNumElements = %d", lpTransport, lpdvHandler, lpUserContext, dwNumElements );
  738. if( lpTransport == NULL )
  739. {
  740. DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid transport" );
  741. return DVERR_INVALIDPOINTER;
  742. }
  743. hr = DV_ValidMessageArray( lpdwMessages, dwNumElements, FALSE );
  744. if( FAILED( hr ) )
  745. {
  746. DPFX(DPFPREP, DVF_ERRORLEVEL, "ValidMessageArray Failed hr = 0x%x", hr );
  747. return hr;
  748. }
  749. DPFX(DPFPREP, DVF_APIPARAM, "Message IDs=%d", dwNumElements );
  750. if( lpdwMessages != NULL )
  751. {
  752. for( DWORD dwIndex = 0; dwIndex < dwNumElements; dwIndex++ )
  753. {
  754. DPFX(DPFPREP, DVF_APIPARAM, "MessageIDs[%d] = %d", dwIndex, lpdwMessages[dwIndex] );
  755. }
  756. }
  757. HANDLE hThread;
  758. // Wait for a write lock on the object
  759. CDVCSLock guardLock(&m_csClassLock);
  760. guardLock.Lock();
  761. if( m_dwCurrentState != DVCSTATE_NOTINITIALIZED )
  762. {
  763. DPFX(DPFPREP, DVF_ERRORLEVEL, "Already Initialized" );
  764. return DVERR_INITIALIZED;
  765. }
  766. if( lpdvHandler == NULL && lpdwMessages != NULL )
  767. {
  768. DPFX(DPFPREP, DVF_ERRORLEVEL, "Cannot specify message mask there is no callback function" );
  769. return DVERR_NOCALLBACK;
  770. }
  771. m_dwLastConnectSent = 0;
  772. m_dwSynchBegin = 0;
  773. SetCurrentState( DVCSTATE_IDLE );
  774. BFCSingleLock slLock( &m_csNotifyLock );
  775. slLock.Lock();
  776. m_lpMessageHandler = lpdvHandler;
  777. hr = InternalSetNotifyMask( lpdwMessages, dwNumElements );
  778. if( FAILED( hr ) )
  779. {
  780. SetCurrentState( DVCSTATE_NOTINITIALIZED );
  781. DPFX(DPFPREP, DVF_ERRORLEVEL, "SetNotifyMask Failed hr=0x%x", hr );
  782. return hr;
  783. }
  784. m_lpSessionTransport = lpTransport;
  785. m_lpUserContext = lpUserContext;
  786. m_dvidLocal = 0;
  787. m_dwActiveCount = 0;
  788. m_thTimerInfo.hPlaybackTimerEvent = NULL;
  789. m_thTimerInfo.lPlaybackCount = 0;
  790. m_thTimerInfo.hRecordTimerEvent = NULL;
  791. m_hConnectAck = CreateEvent( NULL, FALSE, FALSE, NULL );
  792. m_hDisconnectAck = CreateEvent( NULL, TRUE, FALSE, NULL );
  793. m_hNotifyDone = CreateEvent( NULL, FALSE, FALSE, NULL );
  794. m_hNotifyTerminate = CreateEvent( NULL, FALSE, FALSE, NULL );
  795. m_hNotifyChange = CreateEvent( NULL, FALSE, FALSE, NULL );
  796. m_hNotifyDisconnect = CreateEvent( NULL, FALSE, FALSE, NULL );
  797. m_hNotifyConnect = CreateEvent( NULL, FALSE, FALSE, NULL );
  798. if( m_hConnectAck == NULL ||
  799. m_hDisconnectAck == NULL ||
  800. m_hNotifyTerminate == NULL ||
  801. m_hNotifyChange == NULL ||
  802. m_hNotifyDisconnect == NULL ||
  803. m_hNotifyConnect == NULL ||
  804. m_hNotifyDone==NULL)
  805. {
  806. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to create required events" );
  807. goto ERROR_EXIT_INIT;
  808. }
  809. if (FAILED(NotifyQueue_Init()))
  810. {
  811. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to init notify queue" );
  812. goto ERROR_EXIT_INIT;
  813. }
  814. hThread = (HANDLE) _beginthread( NotifyThread, 0, this );
  815. if( hThread == NULL )
  816. {
  817. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to create watcher thread" );
  818. goto ERROR_EXIT_INIT;
  819. }
  820. // 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
  821. DPFX(DPFPREP, DVF_INFOLEVEL, "Notify Thread Started: 0x%p", hThread );
  822. guardLock.Unlock();
  823. DPFX(DPFPREP, DVF_APIPARAM, "Returning DV_OK" );
  824. return DV_OK;
  825. ERROR_EXIT_INIT:
  826. if( m_hConnectAck != NULL )
  827. {
  828. CloseHandle( m_hConnectAck );
  829. m_hConnectAck = NULL;
  830. }
  831. if( m_hDisconnectAck != NULL )
  832. {
  833. CloseHandle( m_hDisconnectAck );
  834. m_hDisconnectAck = NULL;
  835. }
  836. if( m_hNotifyTerminate != NULL )
  837. {
  838. CloseHandle( m_hNotifyTerminate );
  839. m_hNotifyTerminate = NULL;
  840. }
  841. if( m_hNotifyChange != NULL )
  842. {
  843. CloseHandle( m_hNotifyChange );
  844. m_hNotifyChange = NULL;
  845. }
  846. if( m_hNotifyDisconnect != NULL )
  847. {
  848. CloseHandle( m_hNotifyDisconnect );
  849. m_hNotifyDisconnect = NULL;
  850. }
  851. if( m_hNotifyConnect != NULL )
  852. {
  853. CloseHandle( m_hNotifyConnect );
  854. m_hNotifyConnect = NULL;
  855. }
  856. DPFX(DPFPREP, DVF_ERRORLEVEL, "Returning DVERR_GENERIC" );
  857. return DVERR_GENERIC;
  858. }
  859. #undef DPF_MODNAME
  860. #define DPF_MODNAME "CDirectVoiceClientEngine::Connect"
  861. // Connect
  862. //
  863. // Implements the IDirectXVoiceClient::Connect function.
  864. //
  865. // Locks Required:
  866. // - Write Lock
  867. //
  868. // Called By:
  869. // DVC_Connect
  870. //
  871. HRESULT CDirectVoiceClientEngine::Connect( LPDVSOUNDDEVICECONFIG lpSoundDeviceConfig, LPDVCLIENTCONFIG lpClientConfig, DWORD dwFlags )
  872. {
  873. HRESULT hr;
  874. DPFX(DPFPREP, DVF_ENTRYLEVEL, "Enter" );
  875. hr = DV_ValidClientConfig( lpClientConfig );
  876. if( FAILED( hr ) )
  877. {
  878. DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid Client Config hr=0x%x", hr );
  879. return hr;
  880. }
  881. hr = DV_ValidSoundDeviceConfig( lpSoundDeviceConfig, s_lpwfxPlaybackFormat );
  882. if( FAILED( hr ) )
  883. {
  884. DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid Sound Device Config hr=0x%x", hr );
  885. return hr;
  886. }
  887. if( dwFlags & ~(DVFLAGS_SYNC|DVFLAGS_NOQUERY) )
  888. {
  889. DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid flags specified" );
  890. return DVERR_INVALIDFLAGS;
  891. }
  892. DV_DUMP_SDC( lpSoundDeviceConfig );
  893. DV_DUMP_CC( lpClientConfig );
  894. CDVCSLock guardLock(&m_csClassLock);
  895. guardLock.Lock();
  896. if( m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
  897. {
  898. DPFX(DPFPREP, DVF_ERRORLEVEL, "Object not initialized" );
  899. return DVERR_NOTINITIALIZED;
  900. }
  901. if( m_dwCurrentState == DVCSTATE_CONNECTING ||
  902. m_dwCurrentState == DVCSTATE_DISCONNECTING )
  903. {
  904. DPFX(DPFPREP, DVF_ERRORLEVEL, "Already connecting or disconnecting" );
  905. return DVERR_ALREADYPENDING;
  906. }
  907. if( m_dwCurrentState == DVCSTATE_CONNECTED )
  908. {
  909. DPFX(DPFPREP, DVF_ERRORLEVEL, "Already connected" );
  910. return DVERR_CONNECTED;
  911. }
  912. // Copy over the parameters
  913. memcpy( &m_dvSoundDeviceConfig, lpSoundDeviceConfig, sizeof( DVSOUNDDEVICECONFIG ) );
  914. // map the devices
  915. GUID guidTemp;
  916. hr = DV_MapCaptureDevice(&(m_dvSoundDeviceConfig.guidCaptureDevice), &guidTemp);
  917. if (FAILED(hr))
  918. {
  919. DPFX(DPFPREP, DVF_ERRORLEVEL, "DV_MapCaptureDevice failed, code: %i", hr);
  920. return hr;
  921. }
  922. m_dvSoundDeviceConfig.guidCaptureDevice = guidTemp;
  923. hr = DV_MapPlaybackDevice(&(m_dvSoundDeviceConfig.guidPlaybackDevice), &guidTemp);
  924. if (FAILED(hr))
  925. {
  926. DPFX(DPFPREP, DVF_ERRORLEVEL, "DV_MapPlaybackDevice failed, code: %i", hr);
  927. return hr;
  928. }
  929. m_dvSoundDeviceConfig.guidPlaybackDevice = guidTemp;
  930. // Check to ensure setup has been run on these devices
  931. // but only if the NOQUERY flag has not been set.
  932. if (!(dwFlags & DVFLAGS_NOQUERY))
  933. {
  934. hr = SupervisorCheckAudioSetup(
  935. &(m_dvSoundDeviceConfig.guidPlaybackDevice),
  936. &(m_dvSoundDeviceConfig.guidCaptureDevice),
  937. NULL,
  938. DVFLAGS_QUERYONLY);
  939. switch (hr)
  940. {
  941. case DV_FULLDUPLEX:
  942. // great - carry on.
  943. DPFX(DPFPREP, DVF_INFOLEVEL, "Devices have been tested - full duplex ok");
  944. break;
  945. case DV_HALFDUPLEX:
  946. // force on the half duplex flag.
  947. DPFX(DPFPREP, DVF_INFOLEVEL, "Devices have been tested - half duplex only");
  948. m_dvSoundDeviceConfig.dwFlags |= DVSOUNDCONFIG_HALFDUPLEX;
  949. break;
  950. case DVERR_SOUNDINITFAILURE:
  951. // The devices were tested, and failed miserably.
  952. DPFX(DPFPREP, DVF_INFOLEVEL, "Devices have been tested - total failure");
  953. return DVERR_SOUNDINITFAILURE;
  954. break;
  955. case DVERR_RUNSETUP:
  956. // return the run setup code
  957. DPFX(DPFPREP, DVF_ERRORLEVEL, "Devices have not been tested");
  958. return DVERR_RUNSETUP;
  959. default:
  960. // unexpected return code - this is a real error - propogate it back up
  961. DPFX(DPFPREP, DVF_ERRORLEVEL, "SupervisorCheckAudioSetup failed, code: %i", hr);
  962. return hr;
  963. }
  964. }
  965. // RESET Session flags that need to be reset on every connect
  966. m_fSessionLost = FALSE;
  967. m_hrDisconnectResult = DV_OK;
  968. m_fLocalPlayerNotify = FALSE;
  969. m_fLocalPlayerAvailable = FALSE;
  970. m_dwHostOrderID = DVPROTOCOL_HOSTORDER_INVALID;
  971. m_hPlaybackThreadHandle = NULL;
  972. m_hRecordThreadHandle = NULL;
  973. m_thTimerInfo.hPlaybackTimerEvent = NULL;
  974. m_thTimerInfo.lPlaybackCount = 0;
  975. m_thTimerInfo.hRecordTimerEvent = NULL;
  976. m_lpdvfCompressionInfo = NULL;
  977. m_hrConnectResult = DVERR_GENERIC;
  978. m_hrOriginalConnectResult = DVERR_GENERIC;
  979. // Ok not to use interlocked functions as this should be only thread running w/voice
  980. m_iPlayerListReceived = -1;
  981. m_fDisconnecting = FALSE;
  982. ClientStats_Reset();
  983. // Add a reference to incoming objects
  984. if( m_dvSoundDeviceConfig.lpdsPlaybackDevice != NULL )
  985. {
  986. m_dvSoundDeviceConfig.lpdsPlaybackDevice->AddRef();
  987. }
  988. if( m_dvSoundDeviceConfig.lpdsMainBuffer != NULL )
  989. {
  990. m_dvSoundDeviceConfig.lpdsMainBuffer->AddRef();
  991. }
  992. // Add a reference to incoming objects
  993. if( m_dvSoundDeviceConfig.lpdsCaptureDevice != NULL )
  994. {
  995. m_dvSoundDeviceConfig.lpdsCaptureDevice->AddRef();
  996. }
  997. DNEnterCriticalSection( &m_lockPlaybackMode );
  998. m_dwActiveCount = 0;
  999. m_dwEchoState = DVCECHOSTATE_IDLE;
  1000. DNLeaveCriticalSection( &m_lockPlaybackMode );
  1001. // Need to reset disconnect event manually
  1002. ResetEvent( m_hDisconnectAck );
  1003. // This was here to disable capture
  1004. //
  1005. // m_dvSoundDeviceConfig.dwFlags |= DVSOUNDCONFIG_NOFOCUS;
  1006. m_dvSoundDeviceConfig.dwMainBufferFlags |= DSBPLAY_LOOPING;
  1007. memcpy( &m_dvClientConfig, lpClientConfig, sizeof( DVCLIENTCONFIG ) );
  1008. // Check for duplicate objects in the process, re-use existing object if there is one.
  1009. // Also does some sanity checks.
  1010. hr = CheckForDuplicateObjects();
  1011. if( FAILED(hr) )
  1012. {
  1013. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error checking dsound(cap) objects hr=0x%x", hr );
  1014. goto CONNECT_ERROR;
  1015. }
  1016. #ifdef __FORCEFULLDUPLEX
  1017. m_dvSoundDeviceConfig.dwFlags |= DVSOUNDCONFIG_HALFDUPLEX;
  1018. #endif
  1019. if( m_dvClientConfig.dwBufferAggressiveness == DVBUFFERAGGRESSIVENESS_DEFAULT )
  1020. {
  1021. m_dvClientConfig.dwBufferAggressiveness = s_dwDefaultBufferAggressiveness;
  1022. }
  1023. if( m_dvClientConfig.dwBufferQuality == DVBUFFERQUALITY_DEFAULT )
  1024. {
  1025. m_dvClientConfig.dwBufferQuality = s_dwDefaultBufferQuality;
  1026. }
  1027. if( m_dvClientConfig.dwThreshold == DVTHRESHOLD_DEFAULT )
  1028. {
  1029. m_dvClientConfig.dwThreshold = s_dwDefaultSensitivity;
  1030. }
  1031. m_dwMigrateHostOrderID = DVPROTOCOL_HOSTORDER_INVALID;
  1032. m_dwLastConnectSent = 0;
  1033. m_dwSynchBegin = 0;
  1034. SetCurrentState( DVCSTATE_CONNECTING );
  1035. // Initialize the name table
  1036. m_voiceNameTable.Initialize();
  1037. // Initialize bilinks -- if we fail on our connect things won't go south.
  1038. m_dwPlayActiveCount = 0;
  1039. m_blPlayActivePlayers.Initialize();
  1040. m_blPlayAddPlayers.Initialize();
  1041. m_blNotifyActivePlayers.Initialize();
  1042. m_blNotifyAddPlayers.Initialize();
  1043. m_pStatsBlob = &m_stats;
  1044. m_fpPlayers.Initialize(sizeof(CVoicePlayer), CVoicePlayer::PoolAllocFunction,
  1045. NULL, NULL, CVoicePlayer::PoolDeallocFunction);
  1046. hr = SetupInitialBuffers();
  1047. if( FAILED( hr ) )
  1048. {
  1049. DPFX(DPFPREP, DVF_ERRORLEVEL, "SetupBuffersInitial Failed 0x%x", hr );
  1050. goto CONNECT_ERROR;
  1051. }
  1052. hr = m_lpSessionTransport->EnableReceiveHook( m_lpObject, DVTRANSPORT_OBJECTTYPE_CLIENT );
  1053. if( FAILED( hr ) )
  1054. {
  1055. DPFX(DPFPREP, DVF_ERRORLEVEL, "EnableReceiveHook Failed 0x%x", hr );
  1056. goto CONNECT_ERROR;
  1057. }
  1058. m_fConnectAsync = !(dwFlags & DVFLAGS_SYNC);
  1059. m_dvidServer = m_lpSessionTransport->GetServerID();
  1060. m_dvidLocal = m_lpSessionTransport->GetLocalID();
  1061. // Send connect request to the server
  1062. guardLock.Unlock();
  1063. m_dwLastConnectSent = GetTickCount();
  1064. m_dwSynchBegin = m_dwLastConnectSent;
  1065. hr = Send_ConnectRequest();
  1066. DPFX(DPFPREP, DVF_INFOLEVEL, "DVCE::Connect() - Sending Request to server" );
  1067. if( FAILED( hr ) )
  1068. {
  1069. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error on send 0x%x", hr );
  1070. goto CONNECT_ERROR;
  1071. }
  1072. // If the user wants us to wait for the response, wait here.
  1073. if( dwFlags & DVFLAGS_SYNC )
  1074. {
  1075. DPFX(DPFPREP, DVF_INFOLEVEL, "Sync flag, waiting for completion." );
  1076. // Wait until we're called with the appropriate message
  1077. WaitForSingleObject( m_hConnectAck, INFINITE );
  1078. DPFX(DPFPREP, DVF_INFOLEVEL, "Server response received" );
  1079. return GetConnectResult();
  1080. }
  1081. return DVERR_PENDING;
  1082. CONNECT_ERROR:
  1083. // Release any objects we are holding.. if we have created a sound device
  1084. // reference. I.e. we've linked to an inproc sound object
  1085. if( lpSoundDeviceConfig->lpdsPlaybackDevice == NULL &&
  1086. m_dvSoundDeviceConfig.lpdsPlaybackDevice != NULL )
  1087. {
  1088. m_dvSoundDeviceConfig.lpdsPlaybackDevice->Release();
  1089. m_dvSoundDeviceConfig.lpdsPlaybackDevice = NULL;
  1090. }
  1091. if( m_dvSoundDeviceConfig.lpdsMainBuffer != NULL )
  1092. {
  1093. m_dvSoundDeviceConfig.lpdsMainBuffer->Release();
  1094. m_dvSoundDeviceConfig.lpdsMainBuffer = NULL;
  1095. }
  1096. // Release any objects we are holding
  1097. // i.e. we've linked to an inproc sound object
  1098. if( lpSoundDeviceConfig->lpdsCaptureDevice == NULL &&
  1099. m_dvSoundDeviceConfig.lpdsCaptureDevice != NULL )
  1100. {
  1101. m_dvSoundDeviceConfig.lpdsCaptureDevice->Release();
  1102. m_dvSoundDeviceConfig.lpdsCaptureDevice = NULL;
  1103. }
  1104. SetCurrentState( DVCSTATE_IDLE );
  1105. FreeBuffers();
  1106. m_voiceNameTable.DeInitialize((m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_PEER),m_lpUserContext,m_lpMessageHandler);
  1107. m_fpPlayers.DeInitialize();
  1108. return hr;
  1109. }
  1110. #undef DPF_MODNAME
  1111. #define DPF_MODNAME "CDirectVoiceClientEngine::Send_SessionLost"
  1112. HRESULT CDirectVoiceClientEngine::Send_SessionLost()
  1113. {
  1114. PDVPROTOCOLMSG_SESSIONLOST pSessionLost;
  1115. PDVTRANSPORT_BUFFERDESC pBufferDesc;
  1116. LPVOID pvSendContext;
  1117. HRESULT hr;
  1118. pBufferDesc = GetTransmitBuffer( sizeof( DVPROTOCOLMSG_SESSIONLOST ), &pvSendContext );
  1119. if( pBufferDesc == NULL )
  1120. {
  1121. return DVERR_OUTOFMEMORY;
  1122. }
  1123. pSessionLost = (PDVPROTOCOLMSG_SESSIONLOST) pBufferDesc->pBufferData;
  1124. // Send connection request to the server
  1125. pSessionLost->dwType = DVMSGID_SESSIONLOST;
  1126. pSessionLost->hresReason = DVERR_SESSIONLOST;
  1127. // Fixed so that it gets sent
  1128. hr = m_lpSessionTransport->SendToAll( pBufferDesc, pvSendContext, DVTRANSPORT_SEND_GUARANTEED );
  1129. if( hr != DVERR_PENDING && hr != DV_OK )
  1130. {
  1131. DPFX(DPFPREP, 0, "Error sending connect request hr=0x%x", hr );
  1132. ReturnTransmitBuffer( pvSendContext );
  1133. }
  1134. // Pending = OK = expected
  1135. else
  1136. {
  1137. hr = DV_OK;
  1138. }
  1139. return hr;
  1140. }
  1141. #undef DPF_MODNAME
  1142. #define DPF_MODNAME "CDirectVoiceClientEngine::Send_ConnectRequest"
  1143. HRESULT CDirectVoiceClientEngine::Send_ConnectRequest()
  1144. {
  1145. PDVPROTOCOLMSG_CONNECTREQUEST pConnectRequest;
  1146. PDVTRANSPORT_BUFFERDESC pBufferDesc;
  1147. LPVOID pvSendContext;
  1148. HRESULT hr;
  1149. pBufferDesc = GetTransmitBuffer( sizeof( DVPROTOCOLMSG_CONNECTREQUEST ), &pvSendContext );
  1150. if( pBufferDesc == NULL )
  1151. {
  1152. return DVERR_OUTOFMEMORY;
  1153. }
  1154. pConnectRequest = (PDVPROTOCOLMSG_CONNECTREQUEST) pBufferDesc->pBufferData;
  1155. // Send connection request to the server
  1156. pConnectRequest->dwType = DVMSGID_CONNECTREQUEST;
  1157. pConnectRequest->ucVersionMajor = DVPROTOCOL_VERSION_MAJOR;
  1158. pConnectRequest->ucVersionMinor = DVPROTOCOL_VERSION_MINOR;
  1159. pConnectRequest->dwVersionBuild = DVPROTOCOL_VERSION_BUILD;
  1160. hr = m_lpSessionTransport->SendToServer( pBufferDesc, pvSendContext, DVTRANSPORT_SEND_GUARANTEED );
  1161. if( hr != DVERR_PENDING && hr != DV_OK )
  1162. {
  1163. DPFX(DPFPREP, 0, "Error sending connect request hr=0x%x", hr );
  1164. }
  1165. // Pending = OK = expected
  1166. else
  1167. {
  1168. hr = DV_OK;
  1169. }
  1170. return hr;
  1171. }
  1172. #undef DPF_MODNAME
  1173. #define DPF_MODNAME "CDirectVoiceClientEngine::Send_DisconnectRequest"
  1174. HRESULT CDirectVoiceClientEngine::Send_DisconnectRequest()
  1175. {
  1176. PDVPROTOCOLMSG_GENERIC pDisconnectRequest;
  1177. PDVTRANSPORT_BUFFERDESC pBufferDesc;
  1178. LPVOID pvSendContext;
  1179. HRESULT hr;
  1180. pBufferDesc = GetTransmitBuffer( sizeof( DVPROTOCOLMSG_GENERIC ), &pvSendContext );
  1181. if( pBufferDesc == NULL )
  1182. {
  1183. return DVERR_OUTOFMEMORY;
  1184. }
  1185. pDisconnectRequest = (PDVPROTOCOLMSG_GENERIC) pBufferDesc->pBufferData;
  1186. // Send connection request to the server
  1187. pDisconnectRequest->dwType = DVMSGID_DISCONNECT;
  1188. hr = m_lpSessionTransport->SendToServer( pBufferDesc, pvSendContext, DVTRANSPORT_SEND_GUARANTEED );
  1189. if( hr != DVERR_PENDING && hr != DV_OK )
  1190. {
  1191. DPFX(DPFPREP, 0, "Error sending connect request hr=0x%x", hr );
  1192. }
  1193. // Pending = OK = expected
  1194. else
  1195. {
  1196. hr = DV_OK;
  1197. }
  1198. return hr;
  1199. }
  1200. #undef DPF_MODNAME
  1201. #define DPF_MODNAME "CDirectVoiceClientEngine::Send_SettingsConfirm"
  1202. HRESULT CDirectVoiceClientEngine::Send_SettingsConfirm()
  1203. {
  1204. PDVPROTOCOLMSG_SETTINGSCONFIRM pSettingsConfirm;
  1205. PDVTRANSPORT_BUFFERDESC pBufferDesc;
  1206. LPVOID pvSendContext;
  1207. HRESULT hr;
  1208. pBufferDesc = GetTransmitBuffer( sizeof( DVPROTOCOLMSG_SETTINGSCONFIRM ), &pvSendContext );
  1209. if( pBufferDesc == NULL )
  1210. {
  1211. return DVERR_OUTOFMEMORY;
  1212. }
  1213. pSettingsConfirm = (PDVPROTOCOLMSG_SETTINGSCONFIRM) pBufferDesc->pBufferData;
  1214. // Send connection request to the server
  1215. pSettingsConfirm->dwType = DVMSGID_SETTINGSCONFIRM;
  1216. pSettingsConfirm->dwHostOrderID = m_dwHostOrderID;
  1217. pSettingsConfirm->dwFlags = 0;
  1218. if( m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_HALFDUPLEX )
  1219. {
  1220. pSettingsConfirm->dwFlags |= DVPLAYERCAPS_HALFDUPLEX;
  1221. }
  1222. hr = m_lpSessionTransport->SendToServer( pBufferDesc, pvSendContext, DVTRANSPORT_SEND_GUARANTEED );
  1223. if( hr != DVERR_PENDING && hr != DV_OK )
  1224. {
  1225. DPFX(DPFPREP, 0, "Error sending connect request hr=0x%x", hr );
  1226. }
  1227. // Pending = OK = expected
  1228. else
  1229. {
  1230. hr = DV_OK;
  1231. }
  1232. return hr;
  1233. }
  1234. #undef DPF_MODNAME
  1235. #define DPF_MODNAME "CDirectVoiceClientEngine::Disconnect"
  1236. // Disconnect
  1237. //
  1238. // Implements the IDirectXVoiceClient::Disconnect function
  1239. //
  1240. // Locks Required:
  1241. // - Global Lock
  1242. //
  1243. // Called By:
  1244. // DVC_Disconnect
  1245. //
  1246. HRESULT CDirectVoiceClientEngine::Disconnect( DWORD dwFlags )
  1247. {
  1248. DPFX(DPFPREP, DVF_ENTRYLEVEL, "Enter" );
  1249. DPFX(DPFPREP, DVF_APIPARAM, "dwFlags = 0x%x", dwFlags );
  1250. HRESULT hr;
  1251. if( dwFlags & ~(DVFLAGS_SYNC) )
  1252. {
  1253. DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid flags specified" );
  1254. return DVERR_INVALIDFLAGS;
  1255. }
  1256. CDVCSLock guardLock(&m_csClassLock);
  1257. guardLock.Lock();
  1258. if( m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
  1259. {
  1260. DPFX(DPFPREP, DVF_ERRORLEVEL, "Not initialized" );
  1261. return DVERR_NOTINITIALIZED;
  1262. }
  1263. if( m_dwCurrentState == DVCSTATE_CONNECTING )
  1264. {
  1265. m_fDisconnectAsync = !(dwFlags & DVFLAGS_SYNC);
  1266. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "DVCE::Disconnect() Abort connection" );
  1267. // Handle Connect
  1268. SetConnectResult( DVERR_CONNECTABORTED );
  1269. SendConnectResult();
  1270. SetEvent( m_hConnectAck );
  1271. DoSignalDisconnect( DVERR_CONNECTABORTED );
  1272. guardLock.Unlock();
  1273. if( dwFlags & DVFLAGS_SYNC )
  1274. {
  1275. goto DISCONNECT_WAIT;
  1276. }
  1277. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Returning DVERR_CONNECTABORTING" );
  1278. return DVERR_CONNECTABORTING;
  1279. }
  1280. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "State Good.." );
  1281. if( m_dwCurrentState == DVCSTATE_DISCONNECTING )
  1282. {
  1283. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Already disconnecting. Waiting on current event" );
  1284. guardLock.Unlock();
  1285. if( dwFlags & DVFLAGS_SYNC )
  1286. {
  1287. goto DISCONNECT_WAIT;
  1288. }
  1289. return DVERR_ALREADYPENDING;
  1290. }
  1291. else if( m_dwCurrentState != DVCSTATE_CONNECTED )
  1292. {
  1293. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Not Connected" );
  1294. DPFX(DPFPREP, DVF_APIPARAM, "Returning DVERR_NOTCONNECTED" );
  1295. return DVERR_NOTCONNECTED;
  1296. }
  1297. else
  1298. {
  1299. m_fDisconnectAsync = !(dwFlags & DVFLAGS_SYNC);
  1300. m_dwSynchBegin = GetTickCount();
  1301. // Set current state to disconnecting before we release the lock
  1302. SetCurrentState( DVCSTATE_DISCONNECTING );
  1303. guardLock.Unlock();
  1304. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Disconnect request about to be sent" );
  1305. hr = Send_DisconnectRequest();
  1306. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Disconnect request transmitted hr=0x%x", hr );
  1307. guardLock.Lock();
  1308. if( FAILED( hr ) )
  1309. {
  1310. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "DVCE::Disconnect - Error on send 0x%x", hr );
  1311. // Inform notify thread to disconnect, since send failed there won't be a confirm
  1312. SetEvent( m_hNotifyDisconnect );
  1313. hr = DV_OK;
  1314. }
  1315. else
  1316. {
  1317. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Disconnect sent" );
  1318. }
  1319. }
  1320. guardLock.Unlock();
  1321. if( dwFlags & DVFLAGS_SYNC )
  1322. {
  1323. goto DISCONNECT_WAIT;
  1324. }
  1325. DPFX(DPFPREP, DVF_INFOLEVEL, "Returning DVERR_PENDING" );
  1326. return DVERR_PENDING;
  1327. // You should have dropped the Write Loc+k by now
  1328. DISCONNECT_WAIT:
  1329. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Sync flag, waiting for completion." );
  1330. WaitForSingleObject( m_hDisconnectAck, INFINITE );
  1331. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Server response received" );
  1332. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Disconnect Result = 0x%x", m_hrDisconnectResult );
  1333. return m_hrDisconnectResult;
  1334. }
  1335. #undef DPF_MODNAME
  1336. #define DPF_MODNAME "CDirectVoiceClientEngine::GetSessionDesc"
  1337. // GetSessionDesc
  1338. //
  1339. // Retrieves the current session description.
  1340. //
  1341. // Called By:
  1342. // DVC_GetSessionDesc
  1343. //
  1344. // Locks Required:
  1345. // - Global Read Lock
  1346. //
  1347. HRESULT CDirectVoiceClientEngine::GetSessionDesc( PDVSESSIONDESC lpSessionDesc )
  1348. {
  1349. DPFX(DPFPREP, DVF_ENTRYLEVEL, "Enter" );
  1350. // 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
  1351. DPFX(DPFPREP, DVF_APIPARAM, "lpSessionDescBuffer = 0x%p", lpSessionDesc );
  1352. if( lpSessionDesc == NULL || !DNVALID_WRITEPTR(lpSessionDesc,sizeof( DVSESSIONDESC )) )
  1353. {
  1354. DPFX(DPFPREP, DVF_ERRORLEVEL, "Session desc pointer bad" );
  1355. return DVERR_INVALIDPOINTER;
  1356. }
  1357. if( lpSessionDesc->dwSize != sizeof( DVSESSIONDESC ) )
  1358. {
  1359. DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid size on session desc" );
  1360. return DVERR_INVALIDPARAM;
  1361. }
  1362. CDVCSLock guardLock(&m_csClassLock);
  1363. guardLock.Lock();
  1364. if( m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
  1365. {
  1366. DPFX(DPFPREP, DVF_ERRORLEVEL, "Object not initalized" );
  1367. return DVERR_NOTINITIALIZED;
  1368. }
  1369. if( m_dwCurrentState != DVCSTATE_CONNECTED )
  1370. {
  1371. DPFX(DPFPREP, DVF_ERRORLEVEL, "Not connected" );
  1372. return DVERR_NOTCONNECTED;
  1373. }
  1374. memcpy( lpSessionDesc, &m_dvSessionDesc, sizeof( DVSESSIONDESC ) );
  1375. DV_DUMP_SD( (LPDVSESSIONDESC) lpSessionDesc );
  1376. DPFX(DPFPREP, DVF_APIPARAM, "Returning DV_OK" );
  1377. return DV_OK;
  1378. }
  1379. #undef DPF_MODNAME
  1380. #define DPF_MODNAME "CDirectVoiceClientEngine::GetSoundDeviceConfig"
  1381. // GetSoundDeviceConfig
  1382. //
  1383. // Retrieves the current client configuration.
  1384. //
  1385. // Called By:
  1386. // DVC_GetSoundDeviceConfig
  1387. //
  1388. // Locks Required:
  1389. // - Global Read Lock
  1390. HRESULT CDirectVoiceClientEngine::GetSoundDeviceConfig( PDVSOUNDDEVICECONFIG pSoundDeviceConfig, PDWORD pdwBufferSize )
  1391. {
  1392. DPFX(DPFPREP, DVF_ENTRYLEVEL, "Enter" );
  1393. // 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
  1394. DPFX(DPFPREP, DVF_APIPARAM, "pSoundDeviceConfig = 0x%p", pSoundDeviceConfig );
  1395. DWORD dwRequiredSize = sizeof(DVSOUNDDEVICECONFIG);
  1396. if( pdwBufferSize == NULL ||
  1397. !DNVALID_WRITEPTR(pdwBufferSize,sizeof(DWORD)) )
  1398. {
  1399. DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid pointer" );
  1400. return DVERR_INVALIDPOINTER;
  1401. }
  1402. if( pSoundDeviceConfig != NULL &&
  1403. !DNVALID_WRITEPTR(pSoundDeviceConfig,*pdwBufferSize ) )
  1404. {
  1405. DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid pointer" );
  1406. return DVERR_INVALIDPOINTER;
  1407. }
  1408. if( pSoundDeviceConfig != NULL && pSoundDeviceConfig->dwSize != sizeof( DVSOUNDDEVICECONFIG ) )
  1409. {
  1410. DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid Size on clientconfig" );
  1411. return DVERR_INVALIDPARAM;
  1412. }
  1413. CDVCSLock guardLock(&m_csClassLock);
  1414. guardLock.Lock();
  1415. if( m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
  1416. {
  1417. DPFX(DPFPREP, DVF_ERRORLEVEL, "Object not initialized" );
  1418. return DVERR_NOTINITIALIZED;
  1419. }
  1420. if( m_dwCurrentState != DVCSTATE_CONNECTED )
  1421. {
  1422. DPFX(DPFPREP, DVF_ERRORLEVEL, "Not Connected" );
  1423. return DVERR_NOTCONNECTED;
  1424. }
  1425. if( *pdwBufferSize < dwRequiredSize || pSoundDeviceConfig == NULL )
  1426. {
  1427. DPFX(DPFPREP, DVF_ERRORLEVEL, "Buffer too small!" );
  1428. *pdwBufferSize = dwRequiredSize;
  1429. return DVERR_BUFFERTOOSMALL;
  1430. }
  1431. memcpy( pSoundDeviceConfig, &m_dvSoundDeviceConfig, sizeof( DVSOUNDDEVICECONFIG ) );
  1432. DV_DUMP_SDC( pSoundDeviceConfig );
  1433. // # of bytes written
  1434. *pdwBufferSize = dwRequiredSize;
  1435. DPFX(DPFPREP, DVF_ENTRYLEVEL, "End" );
  1436. DPFX(DPFPREP, DVF_APIPARAM, "Returning DV_OK" );
  1437. return DV_OK;
  1438. }
  1439. #undef DPF_MODNAME
  1440. #define DPF_MODNAME "CDirectVoiceClientEngine::GetClientConfig"
  1441. // GetClientConfig
  1442. //
  1443. // Retrieves the current client configuration.
  1444. //
  1445. // Called By:
  1446. // DVC_GetClientConfig
  1447. //
  1448. // Locks Required:
  1449. // - Global Read Lock
  1450. HRESULT CDirectVoiceClientEngine::GetClientConfig( LPDVCLIENTCONFIG lpClientConfig )
  1451. {
  1452. DPFX(DPFPREP, DVF_ENTRYLEVEL, "Enter" );
  1453. // 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
  1454. DPFX(DPFPREP, DVF_APIPARAM, "lpClientConfig = 0x%p", lpClientConfig );
  1455. if( lpClientConfig == NULL ||
  1456. !DNVALID_WRITEPTR(lpClientConfig,sizeof(DVCLIENTCONFIG) ) )
  1457. {
  1458. DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid pointer" );
  1459. return E_POINTER;
  1460. }
  1461. if( lpClientConfig->dwSize != sizeof( DVCLIENTCONFIG ) )
  1462. {
  1463. DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid Size on clientconfig" );
  1464. return DVERR_INVALIDPARAM;
  1465. }
  1466. CDVCSLock guardLock(&m_csClassLock);
  1467. guardLock.Lock();
  1468. if( m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
  1469. {
  1470. DPFX(DPFPREP, DVF_ERRORLEVEL, "Object not initialized" );
  1471. return DVERR_NOTINITIALIZED;
  1472. }
  1473. if( m_dwCurrentState != DVCSTATE_CONNECTED )
  1474. {
  1475. DPFX(DPFPREP, DVF_ERRORLEVEL, "Not Connected" );
  1476. return DVERR_NOTCONNECTED;
  1477. }
  1478. memcpy( lpClientConfig, &m_dvClientConfig, sizeof( DVCLIENTCONFIG ) );
  1479. if( lpClientConfig->dwFlags & DVCLIENTCONFIG_AUTOVOICEACTIVATED )
  1480. {
  1481. lpClientConfig->dwThreshold = DVTHRESHOLD_UNUSED;
  1482. }
  1483. DV_DUMP_CC( lpClientConfig );
  1484. DPFX(DPFPREP, DVF_ENTRYLEVEL, "End" );
  1485. DPFX(DPFPREP, DVF_APIPARAM, "Returning DV_OK" );
  1486. return DV_OK;
  1487. }
  1488. #undef DPF_MODNAME
  1489. #define DPF_MODNAME "CDirectVoiceClientEngine::SetClientConfig"
  1490. // SetClientConfig
  1491. //
  1492. // Sets the current client configuration.
  1493. //
  1494. // Called By:
  1495. // DVC_SetClientConfig
  1496. //
  1497. // Locks Required:
  1498. // - Global Write Lock
  1499. //
  1500. HRESULT CDirectVoiceClientEngine::SetClientConfig( LPDVCLIENTCONFIG lpClientConfig )
  1501. {
  1502. HRESULT hr;
  1503. DPFX(DPFPREP, DVF_ENTRYLEVEL, "Enter" );
  1504. // 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
  1505. DPFX(DPFPREP, DVF_APIPARAM, "lpClientConfig=0x%p", lpClientConfig );
  1506. if( lpClientConfig == NULL ||
  1507. !DNVALID_READPTR(lpClientConfig,sizeof(DVCLIENTCONFIG)))
  1508. {
  1509. DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid pointer" );
  1510. return E_POINTER;
  1511. }
  1512. DV_DUMP_CC( lpClientConfig );
  1513. if( lpClientConfig->dwSize != sizeof( DVCLIENTCONFIG ) )
  1514. {
  1515. DPFX(DPFPREP, DVF_ERRORLEVEL, "DVCE::SetClientConfig() Error parameters" );
  1516. return DVERR_INVALIDPARAM;
  1517. }
  1518. hr = DV_ValidClientConfig( lpClientConfig );
  1519. if( FAILED( hr ) )
  1520. {
  1521. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error validating Clientconfig hr=0x%x", hr );
  1522. return hr;
  1523. }
  1524. CDVCSLock guardLock(&m_csClassLock);
  1525. guardLock.Lock();
  1526. if( m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
  1527. {
  1528. DPFX(DPFPREP, DVF_ERRORLEVEL, "Not initialized" );
  1529. return DVERR_NOTINITIALIZED;
  1530. }
  1531. if( m_dwCurrentState != DVCSTATE_CONNECTED )
  1532. {
  1533. DPFX(DPFPREP, DVF_ERRORLEVEL, "Not connected" );
  1534. return DVERR_NOTCONNECTED;
  1535. }
  1536. BOOL bNotifyChange = FALSE,
  1537. bPlaybackChange = FALSE,
  1538. bRecordChange = FALSE,
  1539. bSensitivityChange = FALSE;
  1540. // If we're not half duplex, take care of the volume settings
  1541. if( !(m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_HALFDUPLEX ) &&
  1542. !(m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_NORECVOLAVAILABLE) )
  1543. {
  1544. if( lpClientConfig->lRecordVolume != DVRECORDVOLUME_LAST)
  1545. {
  1546. m_dvClientConfig.lRecordVolume = lpClientConfig->lRecordVolume;
  1547. m_audioRecordBuffer->SetVolume( m_dvClientConfig.lRecordVolume );
  1548. }
  1549. }
  1550. if( m_dvClientConfig.lPlaybackVolume != lpClientConfig->lPlaybackVolume )
  1551. {
  1552. m_dvClientConfig.lPlaybackVolume = lpClientConfig->lPlaybackVolume;
  1553. SetPlaybackVolume( m_dvClientConfig.lPlaybackVolume );
  1554. }
  1555. if( m_dvClientConfig.dwNotifyPeriod != lpClientConfig->dwNotifyPeriod )
  1556. {
  1557. m_dvClientConfig.dwNotifyPeriod = lpClientConfig->dwNotifyPeriod;
  1558. SetEvent( m_hNotifyChange );
  1559. }
  1560. if( !(lpClientConfig->dwFlags & DVCLIENTCONFIG_MANUALVOICEACTIVATED ) )
  1561. {
  1562. m_dvClientConfig.dwThreshold = DVTHRESHOLD_UNUSED;
  1563. }
  1564. else if( m_dvClientConfig.dwThreshold != lpClientConfig->dwThreshold )
  1565. {
  1566. if( lpClientConfig->dwThreshold == DVTHRESHOLD_DEFAULT )
  1567. {
  1568. m_dvClientConfig.dwThreshold = s_dwDefaultSensitivity;
  1569. }
  1570. else
  1571. {
  1572. m_dvClientConfig.dwThreshold = lpClientConfig->dwThreshold;
  1573. }
  1574. }
  1575. if( (lpClientConfig->dwFlags & DVCLIENTCONFIG_ECHOSUPPRESSION) !=
  1576. (m_dvClientConfig.dwFlags & DVCLIENTCONFIG_ECHOSUPPRESSION) )
  1577. {
  1578. DNEnterCriticalSection( &m_lockPlaybackMode );
  1579. m_dwEchoState = DVCECHOSTATE_IDLE;
  1580. DNLeaveCriticalSection( &m_lockPlaybackMode );
  1581. }
  1582. if( m_dvClientConfig.dwBufferAggressiveness == DVBUFFERAGGRESSIVENESS_DEFAULT )
  1583. {
  1584. m_dvClientConfig.dwBufferAggressiveness = s_dwDefaultBufferAggressiveness;
  1585. }
  1586. if( m_dvClientConfig.dwBufferQuality == DVBUFFERQUALITY_DEFAULT )
  1587. {
  1588. m_dvClientConfig.dwBufferQuality = s_dwDefaultBufferQuality;
  1589. }
  1590. // If we haven't specified NOFOCUS and we're not half duplex
  1591. if( !(m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_HALFDUPLEX) &&
  1592. !(m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_NOFOCUS) )
  1593. {
  1594. // If the settings have changed
  1595. if( (m_dvClientConfig.dwFlags & DVCLIENTCONFIG_RECORDMUTE) !=
  1596. (lpClientConfig->dwFlags & DVCLIENTCONFIG_RECORDMUTE) )
  1597. {
  1598. if( lpClientConfig->dwFlags & DVCLIENTCONFIG_RECORDMUTE )
  1599. {
  1600. DPFX(DPFPREP, DVF_INFOLEVEL, "Record Muted: Yielding focus" );
  1601. hr = m_audioRecordBuffer->YieldFocus();
  1602. }
  1603. else
  1604. {
  1605. DPFX(DPFPREP, DVF_INFOLEVEL, "Record Un-Muted: Attempting to reclaim focus" );
  1606. hr = m_audioRecordBuffer->ClaimFocus();
  1607. }
  1608. if( FAILED( hr ) )
  1609. {
  1610. DPFX(DPFPREP, DVF_WARNINGLEVEL, "Focus set failed hr=0x%x", hr );
  1611. }
  1612. }
  1613. }
  1614. m_dvClientConfig.dwFlags = lpClientConfig->dwFlags;
  1615. m_dvClientConfig.dwNotifyPeriod = lpClientConfig->dwNotifyPeriod;
  1616. guardLock.Unlock();
  1617. DPFX(DPFPREP, DVF_ENTRYLEVEL, "Returning DV_OK" );
  1618. return DV_OK;
  1619. }
  1620. #undef DPF_MODNAME
  1621. #define DPF_MODNAME "CDirectVoiceClientEngine::GetCaps"
  1622. //
  1623. // GetCaps
  1624. //
  1625. // This function retrieves the caps structure for this DirectPlayVoiceClient
  1626. // object.
  1627. //
  1628. // Called By:
  1629. // - DVC_GetCaps
  1630. //
  1631. // Locks Required:
  1632. // - Global Read Lock
  1633. //
  1634. HRESULT CDirectVoiceClientEngine::GetCaps( LPDVCAPS lpCaps )
  1635. {
  1636. DPFX(DPFPREP, DVF_ENTRYLEVEL, "DVCE::GetCaps() Begin" );
  1637. // 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
  1638. DPFX(DPFPREP, DVF_APIPARAM, "Params: lpCaps: 0x%p", lpCaps );
  1639. CDVCSLock guardLock(&m_csClassLock);
  1640. if( lpCaps == NULL ||
  1641. !DNVALID_WRITEPTR(lpCaps,sizeof(DVCAPS)))
  1642. {
  1643. DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid pointer" );
  1644. return DVERR_INVALIDPOINTER;
  1645. }
  1646. if( lpCaps->dwSize != sizeof( DVCAPS ) )
  1647. {
  1648. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error parameters" );
  1649. return DVERR_INVALIDPARAM;
  1650. }
  1651. guardLock.Lock();
  1652. memcpy( lpCaps, &m_dvCaps, sizeof( DVCAPS ) );
  1653. guardLock.Unlock();
  1654. DV_DUMP_CAPS( lpCaps );
  1655. DPFX(DPFPREP, DVF_ENTRYLEVEL, "Done" );
  1656. return DV_OK;
  1657. }
  1658. #undef DPF_MODNAME
  1659. #define DPF_MODNAME "CDirectVoiceClientEngine::GetCompressionTypes"
  1660. //
  1661. // GetCompressionTypes
  1662. //
  1663. // Retrieves configured compression types for this object.
  1664. //
  1665. // Called By:
  1666. // - DVC_GetCompressionTypes
  1667. //
  1668. // Locks Required:
  1669. // - Global Read Lock
  1670. //
  1671. HRESULT CDirectVoiceClientEngine::GetCompressionTypes( LPVOID lpBuffer, LPDWORD lpdwSize, LPDWORD lpdwNumElements, DWORD dwFlags )
  1672. {
  1673. DPFX(DPFPREP, DVF_ENTRYLEVEL, "Begin" );
  1674. // 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
  1675. DPFX(DPFPREP, DVF_APIPARAM, "Params: lpBuffer = 0x%p lpdwSize = 0x%p lpdwNumElements = 0x%p, dwFlags = 0x%x",
  1676. lpBuffer, lpdwSize, lpdwNumElements, dwFlags );
  1677. CDVCSLock guardLock(&m_csClassLock);
  1678. guardLock.Lock();
  1679. HRESULT hres = DVCDB_CopyCompressionArrayToBuffer( lpBuffer, lpdwSize, lpdwNumElements, dwFlags );
  1680. guardLock.Unlock();
  1681. if( hres == DV_OK )
  1682. {
  1683. DV_DUMP_CI( (LPDVCOMPRESSIONINFO) lpBuffer, *lpdwNumElements );
  1684. }
  1685. DPFX(DPFPREP, DVF_ENTRYLEVEL, "Done" );
  1686. return hres;
  1687. }
  1688. #undef DPF_MODNAME
  1689. #define DPF_MODNAME "CDirectVoiceClientEngine::CheckForAndRemoveTarget"
  1690. //
  1691. // CheckForAndRemoveTarget
  1692. //
  1693. // Checks the current target list for the specified ID and removes it from
  1694. // the target list if it is in the target list.
  1695. //
  1696. HRESULT CDirectVoiceClientEngine::CheckForAndRemoveTarget( DVID dvidID )
  1697. {
  1698. HRESULT hr = DV_OK;
  1699. DNEnterCriticalSection( &m_csTargetLock );
  1700. // Search the list of targets
  1701. for( DWORD dwIndex = 0; dwIndex < m_dwNumTargets; dwIndex++ )
  1702. {
  1703. if( m_pdvidTargets[dwIndex] == dvidID )
  1704. {
  1705. if( m_dwNumTargets == 1 )
  1706. {
  1707. hr = InternalSetTransmitTarget( NULL, 0 );
  1708. }
  1709. // We'll re-use the current target array
  1710. else
  1711. {
  1712. // Collapse the list by either ommiting the last element (if the
  1713. // one we want to remove is last, or by moving last element into
  1714. // the place in the list we're removing.
  1715. if( dwIndex+1 != m_dwNumTargets )
  1716. {
  1717. m_pdvidTargets[dwIndex] = m_pdvidTargets[m_dwNumTargets-1];
  1718. }
  1719. hr = InternalSetTransmitTarget( m_pdvidTargets, m_dwNumTargets-1 );
  1720. }
  1721. break;
  1722. }
  1723. }
  1724. DNLeaveCriticalSection( &m_csTargetLock );
  1725. return DV_OK;
  1726. }
  1727. #undef DPF_MODNAME
  1728. #define DPF_MODNAME "CDirectVoiceClientEngine::InternalSetTransmitTarget"
  1729. //
  1730. // InternalSetTransmitTarget
  1731. //
  1732. // Does the work of setting the target. (Assumes values have been validated).
  1733. //
  1734. // This function is safe to pass a pointer to the current target array. It works
  1735. // on a temporary.
  1736. //
  1737. HRESULT CDirectVoiceClientEngine::InternalSetTransmitTarget( PDVID pdvidTargets, DWORD dwNumTargets )
  1738. {
  1739. DWORD dwRequiredSize;
  1740. PBYTE pbDataBuffer;
  1741. PDVMSG_SETTARGETS pdvSetTarget;
  1742. DNEnterCriticalSection( &m_csTargetLock );
  1743. // No targets? set list to NULL
  1744. if( dwNumTargets == 0 )
  1745. {
  1746. // Close memory leak
  1747. //
  1748. // Hawk Bug #
  1749. //
  1750. if( m_pdvidTargets != NULL )
  1751. {
  1752. delete [] m_pdvidTargets;
  1753. }
  1754. m_pdvidTargets = NULL;
  1755. }
  1756. // Otherwise allocate new list and copy
  1757. else
  1758. {
  1759. PDVID pTmpTargetList;
  1760. pTmpTargetList = new DVID[dwNumTargets];
  1761. if( pTmpTargetList == NULL )
  1762. {
  1763. delete [] m_pdvidTargets;
  1764. m_dwNumTargets = 0;
  1765. DPFX(DPFPREP, DVF_ERRORLEVEL, "Memory alloc failure" );
  1766. DNLeaveCriticalSection( &m_csTargetLock );
  1767. return DVERR_OUTOFMEMORY;
  1768. }
  1769. memcpy( pTmpTargetList, pdvidTargets, dwNumTargets*sizeof(DVID) );
  1770. // Kill off old target list
  1771. if( m_pdvidTargets != NULL )
  1772. delete [] m_pdvidTargets;
  1773. m_pdvidTargets = pTmpTargetList;
  1774. }
  1775. m_dwNumTargets = dwNumTargets;
  1776. m_dwTargetVersion++;
  1777. dwRequiredSize = m_dwNumTargets * sizeof( DVID );
  1778. dwRequiredSize += sizeof( DVMSG_SETTARGETS );
  1779. pbDataBuffer = new BYTE[dwRequiredSize];
  1780. if( pbDataBuffer == NULL )
  1781. {
  1782. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error allocating memory!" );
  1783. DNLeaveCriticalSection( &m_csTargetLock );
  1784. return TRUE;
  1785. }
  1786. pdvSetTarget = (PDVMSG_SETTARGETS) pbDataBuffer;
  1787. pdvSetTarget->pdvidTargets = (PDVID) (pbDataBuffer+sizeof(DVMSG_SETTARGETS));
  1788. pdvSetTarget->dwNumTargets = m_dwNumTargets;
  1789. pdvSetTarget->dwSize = sizeof( DVMSG_SETTARGETS );
  1790. memcpy( pdvSetTarget->pdvidTargets, m_pdvidTargets, sizeof(DVID)*m_dwNumTargets );
  1791. NotifyQueue_Add( DVMSGID_SETTARGETS, pdvSetTarget, dwRequiredSize );
  1792. delete [] pbDataBuffer;
  1793. DNLeaveCriticalSection( &m_csTargetLock );
  1794. return DV_OK;
  1795. }
  1796. #undef DPF_MODNAME
  1797. #define DPF_MODNAME "CDirectVoiceClientEngine::SetTransmitTarget"
  1798. //
  1799. // SetTransmitTarget
  1800. //
  1801. // Sets the current transmit target.
  1802. //
  1803. // Called by:
  1804. // - DVC_SetTransmitTarget
  1805. //
  1806. // Locks Required:
  1807. // - Global Write Lock
  1808. //
  1809. HRESULT CDirectVoiceClientEngine::SetTransmitTarget( PDVID pdvidTargets, DWORD dwNumTargets, DWORD dwFlags )
  1810. {
  1811. DPFX(DPFPREP, DVF_ENTRYLEVEL, "Begin" );
  1812. // 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
  1813. DPFX(DPFPREP, DVF_APIPARAM, "Params: pdvidTargets = 0x%p dwNumTargets = %d dwFlags = 0x%x", pdvidTargets, dwNumTargets, dwFlags );
  1814. if( dwFlags != 0 )
  1815. {
  1816. DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid flags" );
  1817. return DVERR_INVALIDFLAGS;
  1818. }
  1819. HRESULT hr;
  1820. // Check that the target list is valid
  1821. hr = DV_ValidTargetList( pdvidTargets, dwNumTargets );
  1822. if( FAILED( hr ) )
  1823. {
  1824. DPFX(DPFPREP, DVF_ERRORLEVEL, "Target list is not valid" );
  1825. return hr;
  1826. }
  1827. DWORD dwIndex;
  1828. if( m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
  1829. {
  1830. DPFX(DPFPREP, DVF_ERRORLEVEL, "Object not initialized" );
  1831. return DVERR_NOTINITIALIZED;
  1832. }
  1833. if( m_dwCurrentState != DVCSTATE_CONNECTED &&
  1834. m_dwCurrentState != DVCSTATE_CONNECTING )
  1835. {
  1836. DPFX(DPFPREP, DVF_ERRORLEVEL, "Not connected" );
  1837. return DVERR_NOTCONNECTED;
  1838. }
  1839. else
  1840. {
  1841. if( m_dvSessionDesc.dwFlags & DVSESSION_SERVERCONTROLTARGET )
  1842. {
  1843. DPFX(DPFPREP, DVF_ERRORLEVEL, "Denied. Server controlled target" );
  1844. return DVERR_NOTALLOWED;
  1845. }
  1846. if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_PEER )
  1847. {
  1848. // Loop through target list, confirm they are valid entries
  1849. for( dwIndex = 0; dwIndex < dwNumTargets; dwIndex++ )
  1850. {
  1851. if( !m_voiceNameTable.IsEntry(pdvidTargets[dwIndex]) )
  1852. {
  1853. if( !m_lpSessionTransport->ConfirmValidGroup( pdvidTargets[dwIndex] ) )
  1854. {
  1855. DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid target" );
  1856. return DVERR_INVALIDTARGET;
  1857. }
  1858. }
  1859. }
  1860. }
  1861. }
  1862. hr = InternalSetTransmitTarget( pdvidTargets, dwNumTargets );
  1863. DPFX(DPFPREP, DVF_ENTRYLEVEL, "Done" );
  1864. return hr;
  1865. }
  1866. #undef DPF_MODNAME
  1867. #define DPF_MODNAME "CDirectVoiceClientEngine::GetTransmitTarget"
  1868. //
  1869. // GetTransmitTarget
  1870. //
  1871. // Retrieves the current transmission target.
  1872. //
  1873. // Called By:
  1874. // - DVC_GetTransmitTarget
  1875. //
  1876. // Locks Required:
  1877. // - Read Lock Required
  1878. //
  1879. HRESULT CDirectVoiceClientEngine::GetTransmitTarget( LPDVID lpdvidTargets, PDWORD pdwNumElements, DWORD dwFlags )
  1880. {
  1881. DPFX(DPFPREP, DVF_ENTRYLEVEL, "Begin" );
  1882. // 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
  1883. DPFX(DPFPREP, DVF_APIPARAM, "Params: lpdvidTargets = 0x%p pdwNumElements = 0x%x dwFlags = 0x%x", lpdvidTargets, pdwNumElements, dwFlags );
  1884. if( pdwNumElements == NULL ||
  1885. !DNVALID_WRITEPTR( pdwNumElements, sizeof( DWORD ) ) )
  1886. {
  1887. DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid pointer to num of elements" );
  1888. return DVERR_INVALIDPOINTER;
  1889. }
  1890. if( pdwNumElements != NULL &&
  1891. *pdwNumElements > 0 &&
  1892. !DNVALID_WRITEPTR( lpdvidTargets, (*pdwNumElements) * sizeof( DVID) ) )
  1893. {
  1894. DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid target list buffer specified" );
  1895. return DVERR_INVALIDPOINTER;
  1896. }
  1897. if( pdwNumElements == NULL )
  1898. {
  1899. DPFX(DPFPREP, DVF_ERRORLEVEL, "Must specify a ptr for # of elements" );
  1900. return DVERR_INVALIDPARAM;
  1901. }
  1902. if( dwFlags != 0 )
  1903. {
  1904. DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid flags" );
  1905. return DVERR_INVALIDFLAGS;
  1906. }
  1907. if( m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
  1908. {
  1909. DPFX(DPFPREP, DVF_ERRORLEVEL, "Object not initialized" );
  1910. return DVERR_NOTINITIALIZED;
  1911. }
  1912. else if( m_dwCurrentState != DVCSTATE_CONNECTED )
  1913. {
  1914. DPFX(DPFPREP, DVF_ERRORLEVEL, "Not Connected" );
  1915. DPFX(DPFPREP, DVF_APIPARAM, "Returning DVERR_NOTCONNECTED" );
  1916. return DVERR_NOTCONNECTED;
  1917. }
  1918. HRESULT hr = DV_OK;
  1919. DNEnterCriticalSection( &m_csTargetLock );
  1920. if( *pdwNumElements < m_dwNumTargets )
  1921. {
  1922. hr = DVERR_BUFFERTOOSMALL;
  1923. }
  1924. else
  1925. {
  1926. memcpy( lpdvidTargets, m_pdvidTargets,m_dwNumTargets*sizeof(DVID) );
  1927. }
  1928. *pdwNumElements = m_dwNumTargets;
  1929. DNLeaveCriticalSection( &m_csTargetLock );
  1930. DPFX(DPFPREP, DVF_ENTRYLEVEL, "Success" );
  1931. return hr;
  1932. }
  1933. #undef DPF_MODNAME
  1934. #define DPF_MODNAME "CDirectVoiceClientEngine::InitSoundTargetList"
  1935. //
  1936. // InitSoundTargetList
  1937. //
  1938. // Initializes the sound target list.
  1939. //
  1940. // Called By:
  1941. // - InitializeSoundSystem
  1942. //
  1943. // Locks Required:
  1944. // - Buffer Lock
  1945. //
  1946. HRESULT CDirectVoiceClientEngine::InitSoundTargetList()
  1947. {
  1948. DNEnterCriticalSection( &m_csBufferLock );
  1949. m_lpstBufferList = NULL;
  1950. DNLeaveCriticalSection( &m_csBufferLock );
  1951. return DV_OK;
  1952. }
  1953. #undef DPF_MODNAME
  1954. #define DPF_MODNAME "CDirectVoiceClientEngine::FreeSoundTargetList"
  1955. //
  1956. // FreeSoundTargetList
  1957. //
  1958. // Releases the sound target list.
  1959. //
  1960. // Also cleans up buffers not released by the user. This must be called before the playback system
  1961. // is shutdown.
  1962. //
  1963. // Called By:
  1964. // - DeInitializeSoundSystem
  1965. //
  1966. // Locks Required:
  1967. // - Buffer Lock
  1968. //
  1969. HRESULT CDirectVoiceClientEngine::FreeSoundTargetList()
  1970. {
  1971. CSoundTarget *lpctFinder, *lpctLast;
  1972. LONG lRefCount;
  1973. DVID dvid;
  1974. DNEnterCriticalSection( &m_csBufferLock );
  1975. lpctLast = NULL;
  1976. lpctFinder = m_lpstBufferList;
  1977. DPFX(DPFPREP, DVF_SOUNDTARGET_DEBUG_LEVEL, "SOUNDTARGET: DESTROYING OPEN TARGET OBJECTS" );
  1978. CHECKLISTINTEGRITY();
  1979. // If we enter this loop we're in a questionable state.
  1980. //
  1981. // The user hasn't called Delete3DSoundBuffer on one or more buffers
  1982. //
  1983. // We're going to cleanup, if they attempt to access the pointers after this point
  1984. // the app will access violate.
  1985. //
  1986. while( lpctFinder != NULL )
  1987. {
  1988. lpctLast = lpctFinder;
  1989. lpctFinder = lpctFinder->m_lpstNext;
  1990. lRefCount = lpctLast->GetRefCount();
  1991. DNASSERT( lRefCount == 2 );
  1992. CHECKLISTINTEGRITY();
  1993. DPFX(DPFPREP, DVF_ERRORLEVEL, "=========================================================================" );
  1994. DPFX(DPFPREP, DVF_ERRORLEVEL, "3D SoundBuffer for ID 0x%x was not released, cleaning it up", lpctLast->GetTarget() );
  1995. DPFX(DPFPREP, DVF_ERRORLEVEL, "This is an ERROR. You must Delete3DSoundBuffer before closing the interface." );
  1996. DPFX(DPFPREP, DVF_ERRORLEVEL, "DirectPlayVoice has freed the resources, so if you access them you will crash." );
  1997. DPFX(DPFPREP, DVF_ERRORLEVEL, "=========================================================================" );
  1998. lpctLast->Release();
  1999. DeleteSoundTarget( lpctLast->GetTarget() );
  2000. CHECKLISTINTEGRITY();
  2001. }
  2002. DPFX(DPFPREP, DVF_SOUNDTARGET_DEBUG_LEVEL, "SOUNDTARGET: DESTROYING GENERAL BUFFER" );
  2003. if( m_lpstGeneralBuffer != NULL )
  2004. {
  2005. // Release the core's reference to the buffer
  2006. lRefCount = m_lpstGeneralBuffer->Release();
  2007. // User must not have freed the buffer
  2008. if( lRefCount > 0 )
  2009. {
  2010. DNASSERT( lRefCount == 1 );
  2011. DPFX(DPFPREP, DVF_ERRORLEVEL, "=========================================================================" );
  2012. DPFX(DPFPREP, DVF_ERRORLEVEL, "Main 3D SoundBuffer was not released, cleaning it up" );
  2013. DPFX(DPFPREP, DVF_ERRORLEVEL, "This is an ERROR. You must Delete3DSoundBuffer before closing the interface." );
  2014. DPFX(DPFPREP, DVF_ERRORLEVEL, "DirectPlayVoice has freed the resources, so if you access them you will crash." );
  2015. DPFX(DPFPREP, DVF_ERRORLEVEL, "=========================================================================" );
  2016. m_lpstGeneralBuffer->Release();
  2017. m_lpstGeneralBuffer = NULL;
  2018. }
  2019. else
  2020. {
  2021. m_lpstGeneralBuffer = NULL;
  2022. }
  2023. // Releasing the buffer above released this object as well
  2024. m_audioPlaybackBuffer = NULL;
  2025. }
  2026. DNLeaveCriticalSection( &m_csBufferLock );
  2027. return DV_OK;
  2028. }
  2029. #undef DPF_MODNAME
  2030. #define DPF_MODNAME "CDirectVoiceClientEngine::AddSoundTarget"
  2031. //
  2032. // AddSoundTarget
  2033. //
  2034. // Adds a new target to the sound target list
  2035. //
  2036. // Called By:
  2037. // - CreateUserBuffer
  2038. //
  2039. // Locks Required:
  2040. // - Buffer Lock
  2041. //
  2042. HRESULT CDirectVoiceClientEngine::AddSoundTarget( CSoundTarget *lpcsTarget )
  2043. {
  2044. DNEnterCriticalSection( &m_csBufferLock );
  2045. DPFX(DPFPREP, DVF_SOUNDTARGET_DEBUG_LEVEL, "SOUNDTARGET: [0x%x] ADDSOUNDTARGET", lpcsTarget->GetTarget() );
  2046. CHECKLISTINTEGRITY();
  2047. lpcsTarget->m_lpstNext = m_lpstBufferList;
  2048. m_lpstBufferList = lpcsTarget;
  2049. CHECKLISTINTEGRITY();
  2050. DNLeaveCriticalSection( &m_csBufferLock );
  2051. return DV_OK;
  2052. }
  2053. #undef DPF_MODNAME
  2054. #define DPF_MODNAME "CDirectVoiceClientEngine::CheckListIntegrity"
  2055. void CDirectVoiceClientEngine::CheckListIntegrity()
  2056. {
  2057. CSoundTarget *lpctFinder;
  2058. DNEnterCriticalSection( &m_csBufferLock );
  2059. lpctFinder = m_lpstBufferList;
  2060. while( lpctFinder != NULL )
  2061. {
  2062. if( lpctFinder != NULL )
  2063. {
  2064. DNASSERT( lpctFinder->m_dwSignature == VSIG_SOUNDTARGET );
  2065. DNASSERT( lpctFinder->GetRefCount() > 0 );
  2066. DNASSERT( lpctFinder->GetRefCount() <= 3 );
  2067. DNASSERT( lpctFinder->GetBuffer() != NULL );
  2068. }
  2069. lpctFinder = lpctFinder->m_lpstNext;
  2070. }
  2071. DNLeaveCriticalSection( &m_csBufferLock );
  2072. }
  2073. #undef DPF_MODNAME
  2074. #define DPF_MODNAME "CDirectVoiceClientEngine::DeleteSoundTarget"
  2075. //
  2076. // DeleteSoundTarget
  2077. //
  2078. // Removes the specified ID's entry from the sound target list
  2079. //
  2080. // Called By:
  2081. // - DeleteSoundTarget
  2082. //
  2083. // Locks Required:
  2084. // - Buffer lock
  2085. //
  2086. HRESULT CDirectVoiceClientEngine::DeleteSoundTarget( DVID dvidID )
  2087. {
  2088. CSoundTarget *lpctFinder, *lpctLast, *lpctNext;
  2089. LONG lRefCount;
  2090. DNEnterCriticalSection( &m_csBufferLock );
  2091. DPFX(DPFPREP, DVF_SOUNDTARGET_DEBUG_LEVEL, "SOUNDTARGET: [0x%x] DELETESOUNDTARGET ", dvidID );
  2092. lpctLast = NULL;
  2093. lpctFinder = m_lpstBufferList;
  2094. while( lpctFinder != NULL )
  2095. {
  2096. CHECKLISTINTEGRITY();
  2097. if( lpctFinder->GetTarget() == dvidID )
  2098. {
  2099. // Store next pointer
  2100. lpctNext = lpctFinder->m_lpstNext;
  2101. // Release the reference the core has
  2102. // If this is the last reference, it destroys the object
  2103. //
  2104. // If user is holding reference this won't destroy
  2105. // the object, the cleanup will.
  2106. //
  2107. lRefCount = lpctFinder->Release();
  2108. // Only remove from list if reference count is 0.
  2109. //
  2110. // Otherwise you end up with buffer replaying old
  2111. // audio
  2112. //
  2113. DPFX(DPFPREP, DVF_SOUNDTARGET_DEBUG_LEVEL, "SOUNDTARGET: [0x%x] REMOVING FROM LIST ", dvidID );
  2114. /* DNASSERT( lRefCount == 0 );
  2115. if( lRefCount == 0 )
  2116. { */
  2117. if( lpctLast == NULL )
  2118. {
  2119. m_lpstBufferList = lpctNext;
  2120. }
  2121. else
  2122. {
  2123. lpctLast->m_lpstNext = lpctNext;
  2124. }
  2125. // }
  2126. CHECKLISTINTEGRITY();
  2127. DNLeaveCriticalSection( &m_csBufferLock );
  2128. return DV_OK;
  2129. }
  2130. lpctLast = lpctFinder;
  2131. lpctFinder = lpctFinder->m_lpstNext;
  2132. }
  2133. DNLeaveCriticalSection( &m_csBufferLock );
  2134. return DVERR_INVALIDPLAYER;
  2135. }
  2136. #undef DPF_MODNAME
  2137. #define DPF_MODNAME "CDirectVoiceClientEngine::FindSoundTarget"
  2138. //
  2139. // FindSoundTarget
  2140. //
  2141. // Look for sound target buffer for the specified user.
  2142. //
  2143. // If it exists, return it in lpcsTarget
  2144. //
  2145. // Called By:
  2146. // - CreateUserBuffer
  2147. //
  2148. // Locks Required:
  2149. // - Buffer Lock
  2150. //
  2151. HRESULT CDirectVoiceClientEngine::FindSoundTarget( DVID dvidID, CSoundTarget **lpcsTarget )
  2152. {
  2153. DNEnterCriticalSection( &m_csBufferLock );
  2154. CHECKLISTINTEGRITY();
  2155. *lpcsTarget = NULL;
  2156. CSoundTarget *lpctFinder;
  2157. lpctFinder = m_lpstBufferList;
  2158. while( lpctFinder != NULL )
  2159. {
  2160. if( lpctFinder->GetTarget() == dvidID )
  2161. {
  2162. *lpcsTarget = lpctFinder;
  2163. lpctFinder->AddRef();
  2164. DNLeaveCriticalSection( &m_csBufferLock );
  2165. return DV_OK;
  2166. }
  2167. lpctFinder = lpctFinder->m_lpstNext;
  2168. }
  2169. CHECKLISTINTEGRITY();
  2170. DNLeaveCriticalSection( &m_csBufferLock );
  2171. return DVERR_INVALIDPLAYER;
  2172. }
  2173. #undef DPF_MODNAME
  2174. #define DPF_MODNAME "CDirectVoiceClientEngine::SetupPlaybackBufferDesc"
  2175. void CDirectVoiceClientEngine::SetupPlaybackBufferDesc( LPDSBUFFERDESC lpdsBufferDesc, LPDSBUFFERDESC lpdsBufferSource )
  2176. {
  2177. DV_SetupBufferDesc( lpdsBufferDesc, lpdsBufferSource, s_lpwfxPlaybackFormat, m_dwUnCompressedFrameSize*m_dwNumPerBuffer );
  2178. }
  2179. #undef DPF_MODNAME
  2180. #define DPF_MODNAME "CDirectVoiceClientEngine::Create3DSoundBuffer"
  2181. //
  2182. // Create3DSoundBuffer
  2183. //
  2184. // Creates a mixing buffer (a sound target) for the specified user ID.
  2185. //
  2186. // Called By:
  2187. // - DVC_CreateUserBuffer
  2188. //
  2189. // Locks Required:
  2190. // - Global Read Lock
  2191. //
  2192. HRESULT CDirectVoiceClientEngine::Create3DSoundBuffer( DVID dvidID, LPDIRECTSOUNDBUFFER lpdsBuffer, DWORD dwPriority, DWORD dwFlags, LPDIRECTSOUND3DBUFFER *lpBuffer )
  2193. {
  2194. DPFX(DPFPREP, DVF_ENTRYLEVEL, "Begin" );
  2195. // 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
  2196. 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 );
  2197. HRESULT hr;
  2198. if( lpBuffer == NULL ||
  2199. !DNVALID_WRITEPTR( lpBuffer, sizeof( LPDIRECTSOUND3DBUFFER ) ) )
  2200. {
  2201. DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid pointer" );
  2202. return DVERR_INVALIDPOINTER;
  2203. }
  2204. if( dvidID == DVID_REMAINING )
  2205. {
  2206. if( lpdsBuffer != NULL )
  2207. {
  2208. DPFX(DPFPREP, DVF_ERRORLEVEL, "Cannot specify a buffer for the DVID_REMAINING buffer" );
  2209. DPFX(DPFPREP, DVF_ERRORLEVEL, "You can set these values from the SoundDeviceConfig structure" );
  2210. return DVERR_INVALIDPARAM;
  2211. }
  2212. if( dwFlags != 0 || dwPriority != 0 )
  2213. {
  2214. DPFX(DPFPREP, DVF_ERRORLEVEL, "Cannot specify non-zero flags for voice management for DVID_REMAINING buffer" );
  2215. DPFX(DPFPREP, DVF_ERRORLEVEL, "You can set these values from the SoundDeviceConfig structure" );
  2216. return DVERR_INVALIDFLAGS;
  2217. }
  2218. }
  2219. else
  2220. {
  2221. hr = DV_ValidBufferSettings( lpdsBuffer, dwPriority, dwFlags, s_lpwfxPlaybackFormat );
  2222. if( FAILED( hr ) )
  2223. {
  2224. DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid settings for buffer description hr=0x%x", hr );
  2225. return hr;
  2226. }
  2227. dwFlags |= DSBPLAY_LOOPING;
  2228. }
  2229. CDVCSLock guardLock(&m_csClassLock);
  2230. guardLock.Lock();
  2231. if( m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
  2232. {
  2233. DPFX(DPFPREP, DVF_ERRORLEVEL, "Not initialized" );
  2234. return DVERR_NOTINITIALIZED;
  2235. }
  2236. else if( m_dwCurrentState != DVCSTATE_CONNECTED )
  2237. {
  2238. DPFX(DPFPREP, DVF_ERRORLEVEL, "Not connected" );
  2239. return DVERR_NOTCONNECTED;
  2240. }
  2241. if( this->m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_MIXING &&
  2242. dvidID != DVID_REMAINING )
  2243. {
  2244. DPFX(DPFPREP, DVF_ERRORLEVEL, "Only DVID_REMAINING can be spatialized in mixing sessions" );
  2245. return DVERR_NOTALLOWED;
  2246. }
  2247. if( dvidID == m_dvidLocal )
  2248. {
  2249. DPFX(DPFPREP, DVF_ERRORLEVEL, "Cannot create buffer for local player!" );
  2250. return DVERR_INVALIDPLAYER;
  2251. }
  2252. if( dvidID != DVID_ALLPLAYERS &&
  2253. dvidID != DVID_REMAINING &&
  2254. !m_voiceNameTable.IsEntry(dvidID) )
  2255. {
  2256. guardLock.Unlock();
  2257. if( !m_lpSessionTransport->ConfirmValidGroup( dvidID ) )
  2258. {
  2259. DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid player/group ID" );
  2260. return DVERR_INVALIDPLAYER;
  2261. }
  2262. guardLock.Lock();
  2263. }
  2264. DWORD dwMode;
  2265. // Handle request for 3d buffer on the main buffer
  2266. if( dvidID == DVID_REMAINING )
  2267. {
  2268. LPDIRECTSOUND3DBUFFER lpds3dTmp;
  2269. lpds3dTmp = m_lpstGeneralBuffer->Get3DBuffer();
  2270. if( lpds3dTmp == NULL )
  2271. {
  2272. DPFX(DPFPREP, DVF_ERRORLEVEL, "No 3d support" );
  2273. return DVERR_NO3DSOUND;
  2274. }
  2275. hr = lpds3dTmp->GetMode( &dwMode );
  2276. if( FAILED( hr ) )
  2277. {
  2278. DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to get 3d buffer mode hr=0x%x", hr );
  2279. return DVERR_GENERIC;
  2280. }
  2281. if( dwMode != DS3DMODE_DISABLE )
  2282. {
  2283. DPFX(DPFPREP, DVF_ERRORLEVEL, "Already have a buffer for specified user" );
  2284. return DVERR_ALREADYBUFFERED;
  2285. }
  2286. // Check return code
  2287. hr = lpds3dTmp->SetMode( DS3DMODE_NORMAL, DS3D_IMMEDIATE );
  2288. if( hr != DV_OK )
  2289. {
  2290. DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to set the mode to activate 3d buffer hr=0x%x", hr );
  2291. return hr;
  2292. }
  2293. // Add a reference for the user (core already has one)
  2294. m_lpstGeneralBuffer->AddRef();
  2295. DPFX(DPFPREP, DVF_ENTRYLEVEL, "Done" );
  2296. *lpBuffer = lpds3dTmp;
  2297. return DV_OK;
  2298. }
  2299. LONG lResult;
  2300. CSoundTarget *lpstTarget = NULL;
  2301. // Check for existing buffer.. if it already exists return it
  2302. // (Note: Adds a reference to the buffer)
  2303. hr = FindSoundTarget( dvidID, &lpstTarget );
  2304. if( hr == DV_OK )
  2305. {
  2306. if( lpstTarget != NULL )
  2307. {
  2308. lResult = lpstTarget->Release();
  2309. DNASSERT( lResult != 0 );
  2310. }
  2311. DPFX(DPFPREP, DVF_ERRORLEVEL, "CreateUserBuffer: Find of buffer failed. hr=0x%x", hr );
  2312. return DVERR_ALREADYBUFFERED;
  2313. }
  2314. if( lpstTarget != NULL )
  2315. {
  2316. lResult = lpstTarget->Release();
  2317. DNASSERT( lResult != 0 );
  2318. DPFX(DPFPREP, DVF_ERRORLEVEL, "CreateUserBuffer: Buffer already available" );
  2319. return DVERR_ALREADYBUFFERED;
  2320. }
  2321. // If the user has given us a buffer
  2322. if( lpdsBuffer )
  2323. {
  2324. DPFX(DPFPREP, DVF_INFOLEVEL, "Creating buffer using user buffer" );
  2325. lpstTarget = new CSoundTarget( dvidID, m_audioPlaybackDevice, lpdsBuffer, (s_lpwfxPlaybackFormat->wBitsPerSample == 8), dwPriority, dwFlags, m_dwUnCompressedFrameSize );
  2326. }
  2327. else
  2328. {
  2329. DSBUFFERDESC dsBufferDesc;
  2330. DPFX(DPFPREP, DVF_INFOLEVEL, "Creating buffer using user buffer" );
  2331. // Fill in appropriate values for the buffer description
  2332. SetupPlaybackBufferDesc( &dsBufferDesc, NULL );
  2333. // Buffer and sound target ref count = 1
  2334. lpstTarget = new CSoundTarget( dvidID, m_audioPlaybackDevice, &dsBufferDesc, dwPriority, dwFlags, m_dwUnCompressedFrameSize );
  2335. }
  2336. if( lpstTarget == NULL )
  2337. {
  2338. DPFX(DPFPREP, DVF_ERRORLEVEL, "CreateUserBuffer: Failed allocating sound target" );
  2339. return DVERR_OUTOFMEMORY;
  2340. }
  2341. hr = lpstTarget->GetInitResult();
  2342. if( FAILED( hr ) )
  2343. {
  2344. DPFX(DPFPREP, DVF_ERRORLEVEL, "CreateUserBuffer: Init of buffer failed. hr=0x%x", hr );
  2345. lpstTarget->Release();
  2346. return hr;
  2347. }
  2348. hr = lpstTarget->StartMix();
  2349. if( FAILED( hr ) )
  2350. {
  2351. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to start the mix on secondary buffer hr=0x%x.", hr );
  2352. lpstTarget->Release();
  2353. return hr;
  2354. }
  2355. // Buffer and sound target ref count = 2
  2356. lpstTarget->AddRef();
  2357. hr = AddSoundTarget( lpstTarget );
  2358. if( FAILED( hr ) )
  2359. {
  2360. DPFX(DPFPREP, DVF_ERRORLEVEL, "CreateUserBuffer: AddTarget failed. hr=0x%x", hr );
  2361. // Destroy reference from above
  2362. lResult = lpstTarget->Release();
  2363. DNASSERT( lResult != 0 );
  2364. // Destroy base reference
  2365. lResult = lpstTarget->Release();
  2366. DNASSERT( lResult == 0 );
  2367. return hr;
  2368. }
  2369. *lpBuffer = lpstTarget->Get3DBuffer();
  2370. DPFX(DPFPREP, DVF_ENTRYLEVEL, "Done" );
  2371. return DV_OK;
  2372. }
  2373. #undef DPF_MODNAME
  2374. #define DPF_MODNAME "CDirectVoiceClientEngine::Delete3DSoundBuffer"
  2375. //
  2376. // Delete3DSoundBuffer
  2377. //
  2378. // Removes the specified ID from the mixer buffer list. Further speech from
  2379. // the specified player will be played in the remaining buffer.
  2380. //
  2381. // Called By:
  2382. // - DVC_DeleteUserBuffer
  2383. //
  2384. // Locks Required:
  2385. // - Global Write Lock
  2386. //
  2387. HRESULT CDirectVoiceClientEngine::Delete3DSoundBuffer( DVID dvidID, LPDIRECTSOUND3DBUFFER *lplpBuffer )
  2388. {
  2389. DPFX(DPFPREP, DVF_ENTRYLEVEL, "Begin" );
  2390. // 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
  2391. DPFX(DPFPREP, DVF_APIPARAM, "Params: dvidID = 0x%x lpBuffer = 0x%p", dvidID, lplpBuffer );
  2392. if( lplpBuffer == NULL ||
  2393. !DNVALID_WRITEPTR( lplpBuffer, sizeof( LPDIRECTSOUND3DBUFFER ) ) )
  2394. {
  2395. DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid pointer" );
  2396. return E_POINTER;
  2397. }
  2398. CDVCSLock guardLock(&m_csClassLock);
  2399. guardLock.Lock();
  2400. if( m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
  2401. {
  2402. DPFX(DPFPREP, DVF_ERRORLEVEL, "Not initialized" );
  2403. return DVERR_NOTINITIALIZED;
  2404. }
  2405. else if( m_dwCurrentState != DVCSTATE_CONNECTED &&
  2406. m_dwCurrentState != DVCSTATE_DISCONNECTING )
  2407. {
  2408. DPFX(DPFPREP, DVF_ERRORLEVEL, "Not connected" );
  2409. return DVERR_NOTCONNECTED;
  2410. }
  2411. HRESULT hr;
  2412. DWORD dwMode;
  2413. LONG lResult;
  2414. // Handle request to disable 3D on the main buffer
  2415. if( dvidID == DVID_REMAINING )
  2416. {
  2417. LPDIRECTSOUND3DBUFFER lpTmpBuffer;
  2418. lpTmpBuffer = m_lpstGeneralBuffer->Get3DBuffer();
  2419. if( lpTmpBuffer == NULL )
  2420. {
  2421. DPFX(DPFPREP, DVF_ERRORLEVEL, "No 3d buffer supported" );
  2422. return DVERR_NOTBUFFERED;
  2423. }
  2424. if( lpTmpBuffer != *lplpBuffer )
  2425. {
  2426. DPFX(DPFPREP, DVF_ERRORLEVEL, "Buffer passed in does not belong to specified id" );
  2427. return DVERR_INVALIDPARAM;
  2428. }
  2429. hr = lpTmpBuffer->GetMode( &dwMode );
  2430. if( FAILED( hr ) )
  2431. {
  2432. DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to get current mode hr=0x%x", hr );
  2433. return DVERR_GENERIC;
  2434. }
  2435. if( dwMode == DS3DMODE_DISABLE )
  2436. {
  2437. DPFX(DPFPREP, DVF_ERRORLEVEL, "Not buffered" );
  2438. return DVERR_NOTBUFFERED;
  2439. }
  2440. // Check return code
  2441. // Add reference
  2442. hr = lpTmpBuffer->SetMode( DS3DMODE_DISABLE, DS3D_IMMEDIATE );
  2443. if( hr != DV_OK )
  2444. {
  2445. DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to set the mode to activate 3d buffer hr=0x%x", hr );
  2446. return DVERR_GENERIC;
  2447. }
  2448. hr = lpTmpBuffer->SetPosition( 0.0, 0.0, 0.0, DS3D_IMMEDIATE );
  2449. // Not a Failure condition.
  2450. if( hr != DV_OK )
  2451. {
  2452. DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to set the position of the 3d buffer hr=0x%x", hr );
  2453. }
  2454. // Remove reference the user has
  2455. lResult = m_lpstGeneralBuffer->Release();
  2456. *lplpBuffer = NULL;
  2457. return DV_OK;
  2458. }
  2459. CSoundTarget *lpstTarget;
  2460. hr = FindSoundTarget( dvidID, &lpstTarget );
  2461. if( FAILED( hr ) )
  2462. {
  2463. DPFX(DPFPREP, DVF_ERRORLEVEL, "CreateUserBuffer: Find of buffer failed. hr=0x%x", hr );
  2464. return DVERR_NOTBUFFERED;
  2465. }
  2466. if( lpstTarget == NULL )
  2467. {
  2468. DPFX(DPFPREP, DVF_ERRORLEVEL, "DeleteUserBuffer: Unable to retrieve user record" );
  2469. return DVERR_NOTBUFFERED;
  2470. }
  2471. if( lpstTarget->Get3DBuffer() != *lplpBuffer )
  2472. {
  2473. DPFX(DPFPREP, DVF_ERRORLEVEL, "Buffer passed in does not belong to specified id" );
  2474. // Get rid of the reference this func has
  2475. lResult = lpstTarget->Release();
  2476. DNASSERT( lResult != 0 );
  2477. return DVERR_INVALIDPARAM;
  2478. }
  2479. DPFX(DPFPREP, DVF_SOUNDTARGET_DEBUG_LEVEL, "SOUNDTARGET: [0x%x] DESTROY3DBUFFER ", dvidID );
  2480. // Get rid of the reference the FindSoundTarget has
  2481. lResult = lpstTarget->Release();
  2482. DNASSERT( lResult != 0 );
  2483. // Get rid of the reference the user has
  2484. lResult = lpstTarget->Release();
  2485. DNASSERT( lResult != 0 );
  2486. // Destroy the last reference (unless there is one outstanding)
  2487. DeleteSoundTarget( dvidID );
  2488. *lplpBuffer = NULL;
  2489. DPFX(DPFPREP, DVF_ENTRYLEVEL, "Done" );
  2490. return DV_OK;
  2491. }
  2492. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  2493. //
  2494. // DIRECTPLAY/NET --> DirectXVoiceClient Interface
  2495. //
  2496. #undef DPF_MODNAME
  2497. #define DPF_MODNAME "CDirectVoiceClientEngine::ReceiveSpeechMessage"
  2498. // ReceiveSpeechMessage
  2499. //
  2500. // Called by DirectPlay/DirectNet when a DirectXVoice message is received
  2501. //
  2502. // Called By:
  2503. // - DV_ReceiveSpeechMessage
  2504. //
  2505. // Locks Required:
  2506. // - None
  2507. //
  2508. BOOL CDirectVoiceClientEngine::ReceiveSpeechMessage( DVID dvidSource, LPVOID lpMessage, DWORD dwSize )
  2509. {
  2510. BOOL fResult;
  2511. PDVPROTOCOLMSG_FULLMESSAGE lpdvFullMessage;
  2512. // if we dont' have at least one byte then we are going to bail
  2513. if ( dwSize < 1 )
  2514. {
  2515. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::ReceiveSpeechMessage() Ignoring zero-byte sized message from=0x%x",
  2516. dvidSource );
  2517. return FALSE;
  2518. }
  2519. lpdvFullMessage = (PDVPROTOCOLMSG_FULLMESSAGE) lpMessage;
  2520. if( !ValidatePacketType( lpdvFullMessage ) )
  2521. {
  2522. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::ReceiveSpeechMessage() Ignoring message with invalid packet type, type=0x%x, from=0x%x",
  2523. lpdvFullMessage->dvGeneric.dwType, dvidSource );
  2524. return FALSE;
  2525. }
  2526. switch( lpdvFullMessage->dvGeneric.dwType )
  2527. {
  2528. case DVMSGID_HOSTMIGRATELEAVE:
  2529. fResult = HandleHostMigrateLeave( dvidSource, static_cast<PDVPROTOCOLMSG_HOSTMIGRATELEAVE>(lpMessage), dwSize );
  2530. break;
  2531. case DVMSGID_HOSTMIGRATED:
  2532. fResult = HandleHostMigrated( dvidSource, static_cast<PDVPROTOCOLMSG_HOSTMIGRATED>(lpMessage),dwSize );
  2533. break;
  2534. case DVMSGID_CONNECTREFUSE:
  2535. fResult = HandleConnectRefuse( dvidSource, static_cast<PDVPROTOCOLMSG_CONNECTREFUSE>(lpMessage), dwSize );
  2536. break;
  2537. case DVMSGID_CONNECTACCEPT:
  2538. fResult = HandleConnectAccept( dvidSource, static_cast<PDVPROTOCOLMSG_CONNECTACCEPT>(lpMessage), dwSize );
  2539. break;
  2540. case DVMSGID_CREATEVOICEPLAYER:
  2541. fResult = HandleCreateVoicePlayer( dvidSource, static_cast<PDVPROTOCOLMSG_PLAYERJOIN>(lpMessage), dwSize );
  2542. break;
  2543. case DVMSGID_DELETEVOICEPLAYER:
  2544. fResult = HandleDeleteVoicePlayer( dvidSource, static_cast<PDVPROTOCOLMSG_PLAYERQUIT>(lpMessage), dwSize );
  2545. break;
  2546. case DVMSGID_SPEECH:
  2547. fResult = HandleSpeech( dvidSource, static_cast<PDVPROTOCOLMSG_SPEECHHEADER>(lpMessage), dwSize );
  2548. break;
  2549. case DVMSGID_SPEECHBOUNCE:
  2550. fResult = HandleSpeechBounce( dvidSource, static_cast<PDVPROTOCOLMSG_SPEECHHEADER>(lpMessage), dwSize );
  2551. break;
  2552. case DVMSGID_SPEECHWITHFROM:
  2553. fResult = HandleSpeechWithFrom( dvidSource, static_cast<PDVPROTOCOLMSG_SPEECHWITHFROM>(lpMessage), dwSize );
  2554. break;
  2555. case DVMSGID_DISCONNECTCONFIRM:
  2556. fResult = HandleDisconnectConfirm( dvidSource, static_cast<PDVPROTOCOLMSG_DISCONNECT>(lpMessage), dwSize);
  2557. break;
  2558. case DVMSGID_SETTARGETS:
  2559. fResult = HandleSetTarget( dvidSource, static_cast<PDVPROTOCOLMSG_SETTARGET>(lpMessage), dwSize );
  2560. break;
  2561. case DVMSGID_SESSIONLOST:
  2562. fResult = HandleSessionLost( dvidSource, static_cast<PDVPROTOCOLMSG_SESSIONLOST>(lpMessage), dwSize );
  2563. break;
  2564. case DVMSGID_PLAYERLIST:
  2565. fResult = HandlePlayerList( dvidSource, static_cast<PDVPROTOCOLMSG_PLAYERLIST>(lpMessage), dwSize );
  2566. break;
  2567. default:
  2568. DPFX(DPFPREP, DVF_WARNINGLEVEL, "DVCE::ReceiveSpeechMessage() Ignoring non-speech message id=0x%x from=0x%x",
  2569. lpdvFullMessage->dvGeneric.dwType, dvidSource );
  2570. return FALSE;
  2571. }
  2572. return fResult;
  2573. }
  2574. #undef DPF_MODNAME
  2575. #define DPF_MODNAME "CDirectVoiceClientEngine::HandleSetTarget"
  2576. // HandleSetTarget
  2577. //
  2578. // Handles server settarget messages. Sets the local target.
  2579. //
  2580. BOOL CDirectVoiceClientEngine::HandleSetTarget( DVID dvidSource, PDVPROTOCOLMSG_SETTARGET lpdvSetTarget, DWORD dwSize )
  2581. {
  2582. HRESULT hr;
  2583. // Confirm that server controlled targetting is enabled. If it isn't then this should be ignored.
  2584. if( !(m_dvSessionDesc.dwFlags & DVSESSION_SERVERCONTROLTARGET) )
  2585. {
  2586. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSetTarget() Ignoring target message in session w/o server controlled targetting from=0x%x",
  2587. dvidSource );
  2588. return FALSE;
  2589. }
  2590. // Ignore set target message unless it is from the server.
  2591. if( dvidSource != m_dvidServer )
  2592. {
  2593. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSetTarget() Ignoring set target from non-server from=0x%x",
  2594. dvidSource );
  2595. return FALSE;
  2596. }
  2597. // check structure size first so that we don't crash by accessing bad data
  2598. if ( dwSize < sizeof( DVPROTOCOLMSG_SETTARGET ) || ( dwSize != (sizeof( DVPROTOCOLMSG_SETTARGET ) + ( lpdvSetTarget->dwNumTargets * sizeof(DVID) ) ) ) )
  2599. {
  2600. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSetTarget() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
  2601. dwSize, dvidSource );
  2602. return FALSE;
  2603. }
  2604. if( lpdvSetTarget->dwNumTargets > DV_MAX_TARGETS )
  2605. {
  2606. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSetTarget() Ignoring message with too many targets, targets=0x%x, from=0x%x",
  2607. lpdvSetTarget->dwNumTargets, dvidSource );
  2608. return FALSE;
  2609. }
  2610. hr = InternalSetTransmitTarget( (DWORD *) &lpdvSetTarget[1], lpdvSetTarget->dwNumTargets );
  2611. DNASSERT( hr == DV_OK );
  2612. return TRUE;
  2613. }
  2614. #undef DPF_MODNAME
  2615. #define DPF_MODNAME "CDirectVoiceClientEngine::HandleHostMigrateLeave"
  2616. BOOL CDirectVoiceClientEngine::HandleHostMigrateLeave( DVID dvidSource, PDVPROTOCOLMSG_HOSTMIGRATELEAVE lpdvHostMigrateLeave, DWORD dwSize )
  2617. {
  2618. // Call RemovePlayer with the ID of the person who sent this,
  2619. // which should be the host.
  2620. DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Triggered by DVMSGID_HOSTMIGRATELEAVE" );
  2621. MigrateHost_RunElection();
  2622. return TRUE;
  2623. }
  2624. #undef DPF_MODNAME
  2625. #define DPF_MODNAME "CDirectVoiceClientEngine::HandleHostMigrated"
  2626. BOOL CDirectVoiceClientEngine::HandleHostMigrated( DVID dvidSource, PDVPROTOCOLMSG_HOSTMIGRATED lpdvHostMigrated, DWORD dwSize )
  2627. {
  2628. HRESULT hr;
  2629. CDVCSLock guardLock(&m_csClassLock);
  2630. guardLock.Lock();
  2631. // We're not yet connected, so we can't send our settings to the server yet.
  2632. // However, because of the write lock we know that when the connection
  2633. // completes we'll have the right host. (Transparently to the client).
  2634. //
  2635. // We need to proceed if we're disconnecting because we need to send a new disconnect confirm
  2636. //
  2637. if( m_dwCurrentState != DVCSTATE_CONNECTED &&
  2638. m_dwCurrentState != DVCSTATE_DISCONNECTING )
  2639. {
  2640. DPFX(DPFPREP, DVF_WARNINGLEVEL, "Could not respond to new host yet, not yet initialized" );
  2641. return TRUE;
  2642. }
  2643. if( dvidSource != m_dvidServer )
  2644. {
  2645. DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Ignoring host migration from 0x%x -- 0x%x is server", dvidSource, m_dvidServer );
  2646. return TRUE;
  2647. }
  2648. guardLock.Unlock();
  2649. if( m_dwCurrentState == DVCSTATE_DISCONNECTING )
  2650. {
  2651. DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Sending NEW host disconnect request" );
  2652. hr = Send_DisconnectRequest();
  2653. if( FAILED( hr ) )
  2654. {
  2655. DPFX(DPFPREP, DVF_ERRORLEVEL, "Internal send failed hr=0x%x", hr );
  2656. }
  2657. else
  2658. {
  2659. hr=DV_OK;
  2660. }
  2661. }
  2662. else
  2663. {
  2664. DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Player 0x%x received host migrated message!", m_dvidLocal );
  2665. DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Sending player confirm message" );
  2666. DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: According to message Player 0x%x is new host", dvidSource );
  2667. hr = Send_SettingsConfirm();
  2668. if( FAILED( hr ) )
  2669. {
  2670. DPFX(DPFPREP, DVF_ERRORLEVEL, "Internal send failed hr=0x%x", hr );
  2671. }
  2672. else
  2673. {
  2674. hr=DV_OK;
  2675. }
  2676. }
  2677. return TRUE;
  2678. }
  2679. #undef DPF_MODNAME
  2680. #define DPF_MODNAME "CDirectVoiceClientEngine::HandleConnectRefuse"
  2681. // HandleConnectRefuse
  2682. //
  2683. // Handles connection refusals
  2684. //
  2685. BOOL CDirectVoiceClientEngine::HandleConnectRefuse( DVID dvidSource, PDVPROTOCOLMSG_CONNECTREFUSE lpdvConnectRefuse, DWORD dwSize )
  2686. {
  2687. if ( dwSize != sizeof( DVPROTOCOLMSG_CONNECTREFUSE ) )
  2688. {
  2689. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleConnectRefuse() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
  2690. dwSize, dvidSource );
  2691. return FALSE;
  2692. }
  2693. // This prevents someone from sending a connect refuse to bump off a valid player
  2694. if( GetCurrentState() != DVCSTATE_CONNECTING )
  2695. {
  2696. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleConnectRefuse() Ignoring connect refuse AFTER connection from=0x%x", dvidSource );
  2697. return FALSE;
  2698. }
  2699. // Prevent processing of this message unless it came from the server
  2700. if( dvidSource != m_dvidServer )
  2701. {
  2702. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleConnectRefuse() Ignoring connect refuse from non-server - from=0x%x", dvidSource );
  2703. return FALSE;
  2704. }
  2705. // Do some brain dead error checking. Should never happen but print
  2706. // some debug spew just in case
  2707. if (SUCCEEDED(lpdvConnectRefuse->hresResult))
  2708. {
  2709. DPFX(DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "CDirectVoiceClientEngine::HandleConnectRefuse but reason given is success!" );
  2710. }
  2711. SetConnectResult( lpdvConnectRefuse->hresResult );
  2712. SetEvent( m_hNotifyConnect );
  2713. return TRUE;
  2714. }
  2715. #undef DPF_MODNAME
  2716. #define DPF_MODNAME "CDirectVoiceClientEngine::DeInitializeClientServer"
  2717. // DeInitializeClientServer
  2718. //
  2719. // This function is responsible for pre-allocating the information
  2720. // for receiving client/server voice.
  2721. //
  2722. void CDirectVoiceClientEngine::DeInitializeClientServer()
  2723. {
  2724. DVPROTOCOLMSG_PLAYERQUIT dvPlayerQuit;
  2725. dvPlayerQuit.dwType = DVMSGID_DELETEVOICEPLAYER;
  2726. dvPlayerQuit.dvidID = m_dvidServer;
  2727. HandleDeleteVoicePlayer( 0, &dvPlayerQuit, sizeof( DVPROTOCOLMSG_PLAYERQUIT ) );
  2728. }
  2729. #undef DPF_MODNAME
  2730. #define DPF_MODNAME "CDirectVoiceClientEngine::InitializeClientServer"
  2731. // InitializeClientServer
  2732. //
  2733. // This function is responsible for pre-allocating the information
  2734. // for receiving client/server voice
  2735. //
  2736. HRESULT CDirectVoiceClientEngine::InitializeClientServer()
  2737. {
  2738. DPFX(DPFPREP, DVF_INFOLEVEL, "DVCE::InitializeClientServer() - Initializing Client/Server Queues" );
  2739. HRESULT hr;
  2740. CVoicePlayer *pNewPlayer;
  2741. QUEUE_PARAMS queueParams;
  2742. pNewPlayer = (CVoicePlayer*)m_fpPlayers.Get();
  2743. if( pNewPlayer == NULL )
  2744. {
  2745. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to allocate new player for client/server" );
  2746. return DVERR_OUTOFMEMORY;
  2747. }
  2748. ASSERT_VPLAYER(pNewPlayer);
  2749. hr = pNewPlayer->Initialize( m_dvidServer, 0, 0, NULL, &m_fpPlayers );
  2750. if( FAILED( hr ) )
  2751. {
  2752. pNewPlayer->Release();
  2753. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to initialize server player record" );
  2754. return hr;
  2755. }
  2756. queueParams.wFrameSize = m_dwCompressedFrameSize;
  2757. queueParams.bInnerQueueSize = m_lpdvfCompressionInfo->wInnerQueueSize;
  2758. queueParams.bMaxHighWaterMark = m_lpdvfCompressionInfo->wMaxHighWaterMark,
  2759. queueParams.iQuality = m_dvClientConfig.dwBufferQuality;
  2760. queueParams.iHops = 2;
  2761. queueParams.iAggr = m_dvClientConfig.dwBufferAggressiveness;
  2762. queueParams.bInitHighWaterMark = 2;
  2763. queueParams.wQueueId = -1;
  2764. queueParams.wMSPerFrame = m_lpdvfCompressionInfo->dwTimeout,
  2765. queueParams.pFramePool = m_pFramePool;
  2766. hr = pNewPlayer->CreateQueue( &queueParams );
  2767. if( FAILED( hr ) )
  2768. {
  2769. pNewPlayer->Release();
  2770. DPFX(DPFPREP, DVF_ERRORLEVEL, "Could not create queue hr=0x%x", hr );
  2771. return hr;
  2772. }
  2773. hr = m_voiceNameTable.AddEntry( m_dvidServer, pNewPlayer );
  2774. if( FAILED( hr ) )
  2775. {
  2776. // Main ref
  2777. pNewPlayer->Release();
  2778. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to add entry to nametable hr=0x%x", hr );
  2779. return TRUE;
  2780. }
  2781. // Add a reference for the player to the "Playback Add List"
  2782. DNEnterCriticalSection( &m_csPlayAddList );
  2783. pNewPlayer->AddRef();
  2784. pNewPlayer->AddToPlayList( &m_blPlayAddPlayers );
  2785. DNLeaveCriticalSection( &m_csPlayAddList );
  2786. // Add a reference for the player to the "Notify Add List"
  2787. DNEnterCriticalSection( &m_csNotifyAddList );
  2788. pNewPlayer->AddRef();
  2789. pNewPlayer->AddToNotifyList( &m_blNotifyAddPlayers );
  2790. DNLeaveCriticalSection( &m_csNotifyAddList );
  2791. pNewPlayer->SetAvailable( TRUE );
  2792. m_fLocalPlayerAvailable = TRUE;
  2793. // Release our personal reference
  2794. pNewPlayer->Release();
  2795. DPFX(DPFPREP, DVF_INFOLEVEL, "DVCE::InitializeClientServer() - Done Initializing Client/Server Queues" );
  2796. return DP_OK;
  2797. }
  2798. #undef DPF_MODNAME
  2799. #define DPF_MODNAME "CDirectVoiceClientEngine::DoConnectResponse"
  2800. void CDirectVoiceClientEngine::DoConnectResponse()
  2801. {
  2802. HRESULT hr = DV_OK;
  2803. CDVCSLock guardLock(&m_csClassLock);
  2804. guardLock.Lock();
  2805. if( m_dwCurrentState != DVCSTATE_CONNECTING )
  2806. {
  2807. DPFX(DPFPREP, DVF_WARNINGLEVEL, "Aborting Connection & server response arrived" );
  2808. return;
  2809. }
  2810. // Work from the default assumption that something will screw up
  2811. SetConnectResult( DVERR_GENERIC );
  2812. ClientStats_Begin();
  2813. #ifndef __DISABLE_SOUND
  2814. // Handle sound initialization
  2815. hr = InitializeSoundSystem();
  2816. if( FAILED(hr) )
  2817. {
  2818. SetConnectResult(hr);
  2819. DPFX(DPFPREP, DVF_ERRORLEVEL, "Sound Initialization Failed hr=0x%x", hr );
  2820. goto EXIT_ERROR;
  2821. }
  2822. #endif
  2823. hr = SetupSpeechBuffer();
  2824. if( FAILED( hr ) )
  2825. {
  2826. SetConnectResult(hr);
  2827. DPFX(DPFPREP, DVF_ERRORLEVEL, "Could not init speech buffers hr=0x%x", hr );
  2828. goto EXIT_ERROR;
  2829. }
  2830. DPFX(DPFPREP, DVF_INFOLEVEL, "DVCE::HandleConnectAccept() - Sound Initialized" );
  2831. // If we're running in client/server we need to pre-create a single buffer
  2832. // and compressor
  2833. if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_MIXING ||
  2834. m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_ECHO )
  2835. {
  2836. hr = InitializeClientServer();
  2837. if( FAILED( hr ) )
  2838. {
  2839. SetConnectResult(hr);
  2840. DPFX(DPFPREP, DVF_ERRORLEVEL, "Could not init client/server hr=0x%x", hr );
  2841. goto EXIT_ERROR;
  2842. }
  2843. }
  2844. // We need to make player available in client/server because there will be no indication
  2845. if( m_dvSessionDesc.dwSessionType != DVSESSIONTYPE_PEER )
  2846. {
  2847. m_fLocalPlayerAvailable = TRUE;
  2848. }
  2849. #ifndef __DISABLE_SOUND
  2850. // Start playback thread
  2851. // Create Thread events
  2852. m_hPlaybackTerminate = CreateEvent( NULL, FALSE, FALSE, NULL );
  2853. m_hPlaybackDone = CreateEvent( NULL, FALSE, FALSE, NULL );
  2854. // Create Semaphores for playback and record
  2855. m_thTimerInfo.hPlaybackTimerEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  2856. m_thTimerInfo.lPlaybackCount = 0;
  2857. m_thTimerInfo.hRecordTimerEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  2858. if( m_hPlaybackTerminate == NULL || m_hPlaybackDone == NULL ||
  2859. m_thTimerInfo.hPlaybackTimerEvent == NULL || m_thTimerInfo.hRecordTimerEvent == NULL )
  2860. {
  2861. DPFX(DPFPREP, DVF_ERRORLEVEL, "Windows error, event create failure." );
  2862. SetConnectResult( DVERR_GENERIC );
  2863. goto EXIT_ERROR;
  2864. }
  2865. // Create Multimedia timer
  2866. m_pTimer = new DvTimer;
  2867. if( m_pTimer == NULL )
  2868. {
  2869. SetConnectResult( DVERR_OUTOFMEMORY );
  2870. goto EXIT_ERROR;
  2871. }
  2872. if( !m_pTimer->Create( m_lpdvfCompressionInfo->dwTimeout / DV_CLIENT_WAKEUP_MULTIPLER,
  2873. &m_thTimerInfo, MixingWakeupProc ) )
  2874. {
  2875. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to create timer." );
  2876. SetConnectResult( DVERR_GENERIC );
  2877. goto EXIT_ERROR;
  2878. }
  2879. m_hPlaybackThreadHandle = (HANDLE) _beginthread( PlaybackThread, 0, this );
  2880. #ifdef __CORE_THREAD_PRIORITY_HIGH
  2881. SetThreadPriority( m_hPlaybackThreadHandle, THREAD_PRIORITY_TIME_CRITICAL );
  2882. #endif
  2883. // 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
  2884. DPFX(DPFPREP, DVF_INFOLEVEL, "DVCE::HandleConnectAccept() - Playback Thread Started: 0x%p", m_hPlaybackThreadHandle );
  2885. if( !(m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_HALFDUPLEX) )
  2886. {
  2887. m_hRecordTerminate = CreateEvent( NULL, FALSE, FALSE, NULL );
  2888. m_hRecordDone = CreateEvent( NULL, FALSE, FALSE, NULL );
  2889. if (m_hRecordTerminate==NULL || m_hRecordDone==NULL)
  2890. {
  2891. DPFX(DPFPREP, DVF_ERRORLEVEL, "Windows error, event create failure. hr=0x%x", GetLastError() );
  2892. SetConnectResult( DVERR_GENERIC );
  2893. goto EXIT_ERROR;
  2894. }
  2895. // Start Record Thread
  2896. m_hRecordThreadHandle = (HANDLE) _beginthread( RecordThread, 0, this );
  2897. #ifdef __CORE_THREAD_PRIORITY_HIGH
  2898. SetThreadPriority( m_hRecordThreadHandle, THREAD_PRIORITY_TIME_CRITICAL );
  2899. #endif
  2900. // 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
  2901. DPFX(DPFPREP, DVF_INFOLEVEL, "DVCE::HandleConnectAccept() - Record Thread Started: 0x%p", m_hRecordThreadHandle );
  2902. }
  2903. else
  2904. {
  2905. m_hRecordTerminate = NULL;
  2906. m_hRecordDone = NULL;
  2907. }
  2908. #endif
  2909. SetCurrentState( DVCSTATE_CONNECTED );
  2910. SetConnectResult(DV_OK);
  2911. DPFX(DPFPREP, DVF_ENTRYLEVEL, "DVCE::HandleConnectAccept() Success" );
  2912. SendConnectResult();
  2913. ///////
  2914. guardLock.Unlock();
  2915. hr = Send_SettingsConfirm();
  2916. if( FAILED( hr ) )
  2917. {
  2918. DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to send connect confirmation hr=0x%x", hr );
  2919. DPFX(DPFPREP, DVF_ERRORLEVEL, "Other threads will cleanup because connection must be gone" );
  2920. }
  2921. SetEvent( m_hConnectAck );
  2922. return;
  2923. EXIT_ERROR:
  2924. if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_MIXING ||
  2925. m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_ECHO )
  2926. {
  2927. DeInitializeClientServer();
  2928. }
  2929. DPFX(DPFPREP, DVF_ERRORLEVEL, "HandleConnectAccept Failed hr=0x%x", GetConnectResult() );
  2930. DV_DUMP_GUID( m_dvSessionDesc.guidCT );
  2931. guardLock.Unlock();
  2932. Cleanup();
  2933. SendConnectResult();
  2934. SetEvent( m_hConnectAck );
  2935. return;
  2936. }
  2937. #undef DPF_MODNAME
  2938. #define DPF_MODNAME "CDirectVoiceClientEngine::HandleConnectAccept"
  2939. // HandleConnectAccepts
  2940. //
  2941. // Handles connect accepts. Sets connected flag, finishes initialization, informs the
  2942. // connect function to proceed (if it's waiting).
  2943. //
  2944. BOOL CDirectVoiceClientEngine::HandleConnectAccept( DVID dvidSource, PDVPROTOCOLMSG_CONNECTACCEPT lpdvConnectAccept, DWORD dwSize )
  2945. {
  2946. char tmpString[100];
  2947. if ( dwSize != sizeof( DVPROTOCOLMSG_CONNECTACCEPT ) )
  2948. {
  2949. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleConnectAccept() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
  2950. dwSize, dvidSource );
  2951. return FALSE;
  2952. }
  2953. if( !ValidateSessionType( lpdvConnectAccept->dwSessionType ) )
  2954. {
  2955. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleConnectAccept() Ignoring message with invalid session type, type=0x%x, from=0x%x",
  2956. lpdvConnectAccept->dwSessionType, dvidSource );
  2957. return FALSE;
  2958. }
  2959. if( !ValidateSessionFlags( lpdvConnectAccept->dwSessionFlags ) )
  2960. {
  2961. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleConnectAccept() Ignoring message with invalid session flags, flags=0x%x, from=0x%x",
  2962. lpdvConnectAccept->dwSessionFlags, dvidSource );
  2963. return FALSE;
  2964. }
  2965. DPFX(DPFPREP, DVF_ENTRYLEVEL, "DVCE::HandleConnectAccept() Entry" );
  2966. CDVCSLock guardLock(&m_csClassLock);
  2967. guardLock.Lock();
  2968. // We're already connected, server is responding to the earlier request
  2969. if( m_dwCurrentState != DVCSTATE_CONNECTING )
  2970. {
  2971. return TRUE;
  2972. }
  2973. m_hPlaybackTerminate = NULL;
  2974. m_hPlaybackDone = NULL;
  2975. m_thTimerInfo.hPlaybackTimerEvent = NULL;
  2976. m_thTimerInfo.hRecordTimerEvent = NULL;
  2977. m_pTimer = NULL;
  2978. // Inform transport layer who the server is. (So it no longer thinks it's DPID_ALL).
  2979. m_lpSessionTransport->MigrateHost( dvidSource );
  2980. m_dvidServer = m_lpSessionTransport->GetServerID();
  2981. DVPROTOCOLMSG_FULLMESSAGE dvMessage;
  2982. DPFX(DPFPREP, DVF_INFOLEVEL, "Connect Accept Received" );
  2983. m_dvSessionDesc.dwSize = sizeof( DVSESSIONDESC );
  2984. m_dvSessionDesc.dwBufferAggressiveness = 0;
  2985. m_dvSessionDesc.dwBufferQuality = 0;
  2986. m_dvSessionDesc.dwFlags = lpdvConnectAccept->dwSessionFlags;
  2987. m_dvSessionDesc.guidCT = lpdvConnectAccept->guidCT;
  2988. m_dvSessionDesc.dwSessionType = lpdvConnectAccept->dwSessionType;
  2989. HRESULT hr = DVCDB_GetCompressionInfo( m_dvSessionDesc.guidCT, &m_lpdvfCompressionInfo );
  2990. if( FAILED( hr ) )
  2991. {
  2992. SetConnectResult( DVERR_COMPRESSIONNOTSUPPORTED );
  2993. DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid Compression Type" );
  2994. SetEvent( m_hNotifyConnect );
  2995. return TRUE;
  2996. }
  2997. SetConnectResult( DV_OK );
  2998. DV_DUMP_CIF( m_lpdvfCompressionInfo, 1 );
  2999. SetEvent( m_hNotifyConnect );
  3000. return TRUE;
  3001. }
  3002. #undef DPF_MODNAME
  3003. #define DPF_MODNAME "CDirectVoiceClientEngine::HandlePlayerList"
  3004. BOOL CDirectVoiceClientEngine::HandlePlayerList( DVID dvidSource, PDVPROTOCOLMSG_PLAYERLIST lpdvPlayerList, DWORD dwSize )
  3005. {
  3006. DVPROTOCOLMSG_PLAYERJOIN dvMsgPlayerJoin; // Used to fake out HandleCreateVoicePlayer
  3007. DWORD dwIndex;
  3008. // Prevent player list path in non-peer-to-peer sessions
  3009. if( m_dvSessionDesc.dwSessionType != DVSESSIONTYPE_PEER )
  3010. {
  3011. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "Ignoring playerlist in non-peer session from=0x%x", dvidSource );
  3012. return FALSE;
  3013. }
  3014. // Prevent player list from non-server.
  3015. if( dvidSource != m_dvidServer )
  3016. {
  3017. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "Ignoring player list from non-host - from=0x%x", dvidSource );
  3018. return FALSE;
  3019. }
  3020. // check structure size first so that we don't crash by accessing bad data
  3021. if ( dwSize < sizeof( DVPROTOCOLMSG_PLAYERLIST ) || ( dwSize != (sizeof( DVPROTOCOLMSG_PLAYERLIST ) + ( lpdvPlayerList->dwNumEntries * sizeof(DVPROTOCOLMSG_PLAYERLIST_ENTRY ) ) ) ) )
  3022. {
  3023. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandlePlayerList() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
  3024. dwSize, dvidSource );
  3025. return FALSE;
  3026. }
  3027. // Should transition this from -1 to 0 if this is the first time, otherwise it will be positive
  3028. if( InterlockedIncrement( &m_iPlayerListReceived ) != 0 )
  3029. {
  3030. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "Ignoring dup player list - from=0x%x", dvidSource );
  3031. return FALSE;
  3032. }
  3033. const DVPROTOCOLMSG_PLAYERLIST_ENTRY *pdvPlayerList = (DVPROTOCOLMSG_PLAYERLIST_ENTRY *) &lpdvPlayerList[1];
  3034. // Get our host order ID
  3035. m_dwHostOrderID = lpdvPlayerList->dwHostOrderID;
  3036. DPFX(DPFPREP, DVF_INFOLEVEL, "Received player list. Unpacking %d entries", lpdvPlayerList->dwNumEntries );
  3037. for( dwIndex = 0; dwIndex < lpdvPlayerList->dwNumEntries; dwIndex++ )
  3038. {
  3039. dvMsgPlayerJoin.dwType = DVMSGID_CREATEVOICEPLAYER;
  3040. dvMsgPlayerJoin.dvidID = pdvPlayerList[dwIndex].dvidID;
  3041. dvMsgPlayerJoin.dwFlags = pdvPlayerList[dwIndex].dwPlayerFlags;
  3042. dvMsgPlayerJoin.dwHostOrderID = pdvPlayerList[dwIndex].dwHostOrderID;
  3043. if( !InternalCreateVoicePlayer( dvidSource, &dvMsgPlayerJoin, sizeof( DVPROTOCOLMSG_PLAYERJOIN ) ) )
  3044. {
  3045. DPFX(DPFPREP, DVF_ERRORLEVEL, "Handle voice player failed during unpack" );
  3046. return FALSE;
  3047. }
  3048. }
  3049. return TRUE;
  3050. }
  3051. #undef DPF_MODNAME
  3052. #define DPF_MODNAME "CDirectVoiceClientEngine::NotifyComplete_SyncWait"
  3053. //
  3054. // NotifyComplete_SyncWait
  3055. //
  3056. // This is a completion function for notifications which need to be performed synchronously.
  3057. //
  3058. void CDirectVoiceClientEngine::NotifyComplete_SyncWait( PVOID pvContext, CNotifyElement *pElement )
  3059. {
  3060. HANDLE *pTmpHandle = (HANDLE *) pvContext;
  3061. DNASSERT( pTmpHandle != NULL );
  3062. if( pTmpHandle != NULL && *pTmpHandle != NULL )
  3063. {
  3064. SetEvent( *pTmpHandle );
  3065. }
  3066. }
  3067. #undef DPF_MODNAME
  3068. #define DPF_MODNAME "CDirectVoiceClientEngine::NotifyComplete_RemotePlayer"
  3069. //
  3070. // NotifyComplete_RemotePlayer
  3071. //
  3072. // This is a completion function for when notification of a new remote player has been processed
  3073. //
  3074. void CDirectVoiceClientEngine::NotifyComplete_RemotePlayer( PVOID pvContext, CNotifyElement *pElement )
  3075. {
  3076. CVoicePlayer *pPlayer = (CVoicePlayer *) pvContext;
  3077. ASSERT_VPLAYER(pPlayer);
  3078. PDVMSG_CREATEVOICEPLAYER pCreatePlayer = NULL;
  3079. if( pElement->m_etElementType == NOTIFY_DYNAMIC )
  3080. {
  3081. pCreatePlayer = (PDVMSG_CREATEVOICEPLAYER) pElement->m_element.dynamic.m_lpData;
  3082. }
  3083. else
  3084. {
  3085. pCreatePlayer = (PDVMSG_CREATEVOICEPLAYER) pElement->m_element.fixed.m_bFixedHolder;
  3086. }
  3087. DNASSERT( pPlayer != NULL );
  3088. DNASSERT( pCreatePlayer->dwSize == sizeof( DVMSG_CREATEVOICEPLAYER ) );
  3089. pPlayer->SetContext( pCreatePlayer->pvPlayerContext );
  3090. pPlayer->SetAvailable( TRUE );
  3091. pPlayer->Release();
  3092. }
  3093. #undef DPF_MODNAME
  3094. #define DPF_MODNAME "CDirectVoiceClientEngine::NotifyComplete_LocalPlayer"
  3095. //
  3096. // NotifyComplete_LocalPlayer
  3097. //
  3098. // This is a completion function for when notification of the local player has been processed
  3099. //
  3100. void CDirectVoiceClientEngine::NotifyComplete_LocalPlayer( PVOID pvContext, CNotifyElement *pElement )
  3101. {
  3102. CDirectVoiceClientEngine *pvEngine = (CDirectVoiceClientEngine *) pvContext;
  3103. PDVMSG_CREATEVOICEPLAYER pCreatePlayer = NULL;
  3104. if( pElement->m_etElementType == NOTIFY_DYNAMIC )
  3105. {
  3106. pCreatePlayer = (PDVMSG_CREATEVOICEPLAYER) pElement->m_element.dynamic.m_lpData;
  3107. }
  3108. else
  3109. {
  3110. pCreatePlayer = (PDVMSG_CREATEVOICEPLAYER) pElement->m_element.fixed.m_bFixedHolder;
  3111. }
  3112. DNASSERT( pCreatePlayer->dwSize == sizeof( DVMSG_CREATEVOICEPLAYER ) );
  3113. pvEngine->m_pvLocalPlayerContext = pCreatePlayer->pvPlayerContext;
  3114. pvEngine->m_fLocalPlayerAvailable = TRUE;
  3115. }
  3116. #undef DPF_MODNAME
  3117. #define DPF_MODNAME "CDirectVoiceClientEngine::HandleCreateVoicePlayer"
  3118. //
  3119. // HandleCreateVoicePlayer
  3120. //
  3121. // Validates createvoice player message, source and session type for incoming createvoiceplayer messages.
  3122. // If the message is valid, it gets processed.
  3123. //
  3124. BOOL CDirectVoiceClientEngine::HandleCreateVoicePlayer( DVID dvidSource, PDVPROTOCOLMSG_PLAYERJOIN lpdvCreatePlayer, DWORD dwSize )
  3125. {
  3126. if ( dwSize != sizeof( DVPROTOCOLMSG_PLAYERJOIN ) )
  3127. {
  3128. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleCreateVoicePlayer() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
  3129. dwSize, dvidSource );
  3130. return FALSE;
  3131. }
  3132. // Prevent createplayer path in non-peer-to-peer sessions
  3133. if( m_dvSessionDesc.dwSessionType != DVSESSIONTYPE_PEER )
  3134. {
  3135. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleCreateVoicePlayer() Ignoring create player in non-peer session from=0x%x", dvidSource );
  3136. return FALSE;
  3137. }
  3138. // Prevent createplayer from non-server.
  3139. if( dvidSource != m_dvidServer )
  3140. {
  3141. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleCreateVoicePlayer() Ignoring create player from non-host - from=0x%x", dvidSource );
  3142. return FALSE;
  3143. }
  3144. return InternalCreateVoicePlayer( dvidSource, lpdvCreatePlayer, dwSize );
  3145. }
  3146. #undef DPF_MODNAME
  3147. #define DPF_MODNAME "CDirectVoiceClientEngine::HandleCreateVoicePlayer"
  3148. //
  3149. // InternalCreateVoicePlayer
  3150. //
  3151. // Performs initialization required to create the specified user's record. This function does very little validity checking,
  3152. // the CALLER should do this. This should not be called directly from the wire unless the source and state have
  3153. // been validated.
  3154. //
  3155. // Players in the system will normall have a reference count of 3:
  3156. // - 1 for playback thread
  3157. // - 1 for notify thread
  3158. // - 1 for nametable
  3159. //
  3160. // When a player is added they are added to the nametable as well as the
  3161. // pending lists for notify thread and playback thread.
  3162. //
  3163. // Both of these threads wakeup and:
  3164. // - Add any players on the "add list"
  3165. // - Remove any players who are marked disconnecting
  3166. //
  3167. BOOL CDirectVoiceClientEngine::InternalCreateVoicePlayer( DVID dvidSource, PDVPROTOCOLMSG_PLAYERJOIN lpdvCreatePlayer, DWORD dwSize )
  3168. {
  3169. if( !ValidatePlayerFlags( lpdvCreatePlayer->dwFlags ) )
  3170. {
  3171. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleCreateVoicePlayer() Ignoring message with invalid player flags, flags=0x%x, from=0x%x",
  3172. lpdvCreatePlayer->dwFlags, dvidSource );
  3173. return FALSE;
  3174. }
  3175. if( !ValidatePlayerDVID( lpdvCreatePlayer->dvidID ) )
  3176. {
  3177. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleCreateVoicePlayer() Ignoring message with invalid player dvid, flags=0x%x, from=0x%x",
  3178. lpdvCreatePlayer->dvidID, dvidSource );
  3179. return FALSE;
  3180. }
  3181. if( m_dwCurrentState != DVCSTATE_CONNECTED )
  3182. return TRUE;
  3183. CVoicePlayer *newPlayer;
  3184. HRESULT hr;
  3185. QUEUE_PARAMS queueParams;
  3186. hr = m_voiceNameTable.GetEntry( lpdvCreatePlayer->dvidID, &newPlayer, TRUE );
  3187. // Ignore duplicate players
  3188. if( hr == DV_OK )
  3189. {
  3190. ASSERT_VPLAYER(newPlayer);
  3191. newPlayer->Release();
  3192. return TRUE;
  3193. }
  3194. DPFX(DPFPREP, DVF_CONNECT_PROCEDURE_DEBUG_LEVEL, "Received Create for player ID 0x%x",lpdvCreatePlayer->dvidID );
  3195. // Do not both creating a queue or a player entry for ourselves
  3196. // Not needed
  3197. if( lpdvCreatePlayer->dvidID != m_lpSessionTransport->GetLocalID() )
  3198. {
  3199. DPFX(DPFPREP, DVF_CONNECT_PROCEDURE_DEBUG_LEVEL, "Creating player record" );
  3200. newPlayer = (CVoicePlayer*)m_fpPlayers.Get();
  3201. if( newPlayer == NULL )
  3202. {
  3203. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to allocate new player record. Alloc failure" );
  3204. return TRUE;
  3205. }
  3206. ASSERT_VPLAYER(newPlayer);
  3207. hr = newPlayer->Initialize( lpdvCreatePlayer->dvidID, lpdvCreatePlayer->dwHostOrderID, lpdvCreatePlayer->dwFlags, NULL, &m_fpPlayers );
  3208. if( FAILED( hr ) )
  3209. {
  3210. newPlayer->Release();
  3211. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to initialize player record hr=0x%x", hr );
  3212. return TRUE;
  3213. }
  3214. queueParams.wFrameSize = m_dwCompressedFrameSize;
  3215. queueParams.bInnerQueueSize = m_lpdvfCompressionInfo->wInnerQueueSize;
  3216. queueParams.bMaxHighWaterMark = m_lpdvfCompressionInfo->wMaxHighWaterMark,
  3217. queueParams.iQuality = m_dvClientConfig.dwBufferQuality;
  3218. queueParams.iHops = 1;
  3219. queueParams.iAggr = m_dvClientConfig.dwBufferAggressiveness;
  3220. queueParams.bInitHighWaterMark = 2;
  3221. queueParams.wQueueId = lpdvCreatePlayer->dvidID;
  3222. queueParams.wMSPerFrame = m_lpdvfCompressionInfo->dwTimeout,
  3223. queueParams.pFramePool = m_pFramePool;
  3224. hr = newPlayer->CreateQueue( &queueParams );
  3225. if( FAILED( hr ) )
  3226. {
  3227. newPlayer->Release();
  3228. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to create queue for player hr=0x%x", hr );
  3229. return TRUE;
  3230. }
  3231. hr = m_voiceNameTable.AddEntry( lpdvCreatePlayer->dvidID, newPlayer );
  3232. if( FAILED( hr ) )
  3233. {
  3234. // Main ref
  3235. newPlayer->Release();
  3236. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to add entry to nametable hr=0x%x", hr );
  3237. return TRUE;
  3238. }
  3239. if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_FORWARDING )
  3240. {
  3241. newPlayer->SetAvailable(TRUE);
  3242. }
  3243. // Add a reference for the player to the "Playback Add List"
  3244. DNEnterCriticalSection( &m_csPlayAddList );
  3245. newPlayer->AddRef();
  3246. newPlayer->AddToPlayList( &m_blPlayAddPlayers );
  3247. DNLeaveCriticalSection( &m_csPlayAddList );
  3248. // Add a reference for the player to the "Notify Add List"
  3249. DNEnterCriticalSection( &m_csNotifyAddList );
  3250. newPlayer->AddRef();
  3251. newPlayer->AddToNotifyList( &m_blNotifyAddPlayers );
  3252. DNLeaveCriticalSection( &m_csNotifyAddList );
  3253. // This will now be released by the callback, unless this is multicast
  3254. //
  3255. // Release our personal reference
  3256. if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_FORWARDING )
  3257. {
  3258. newPlayer->Release();
  3259. }
  3260. }
  3261. else
  3262. {
  3263. DPFX(DPFPREP, DVF_CONNECT_PROCEDURE_DEBUG_LEVEL, "Local player, no player record required" );
  3264. }
  3265. if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_PEER )
  3266. {
  3267. DVMSG_CREATEVOICEPLAYER dvCreatePlayer;
  3268. dvCreatePlayer.dvidPlayer = lpdvCreatePlayer->dvidID;
  3269. dvCreatePlayer.dwFlags = lpdvCreatePlayer->dwFlags;
  3270. dvCreatePlayer.dwSize = sizeof( DVMSG_CREATEVOICEPLAYER );
  3271. dvCreatePlayer.pvPlayerContext = NULL;
  3272. // Prevents double notification for local player
  3273. if( lpdvCreatePlayer->dvidID != m_lpSessionTransport->GetLocalID() )
  3274. {
  3275. NotifyQueue_Add( DVMSGID_CREATEVOICEPLAYER, &dvCreatePlayer, sizeof( DVMSG_CREATEVOICEPLAYER ),
  3276. newPlayer, NotifyComplete_RemotePlayer );
  3277. }
  3278. else if( !m_fLocalPlayerNotify )
  3279. {
  3280. // Add local player flag to notification
  3281. dvCreatePlayer.dwFlags |= DVPLAYERCAPS_LOCAL;
  3282. // Notify of local player (don't create player record)
  3283. NotifyQueue_Add( DVMSGID_CREATEVOICEPLAYER, &dvCreatePlayer, sizeof( DVMSG_CREATEVOICEPLAYER ),
  3284. this, NotifyComplete_LocalPlayer );
  3285. m_fLocalPlayerNotify = TRUE;
  3286. }
  3287. }
  3288. return TRUE;
  3289. }
  3290. #undef DPF_MODNAME
  3291. #define DPF_MODNAME "CDirectVoiceClientEngine::MigrateHost_RunElection"
  3292. //
  3293. // MigrateHost_RunElection
  3294. //
  3295. // Runs the host migration algorithm
  3296. //
  3297. HRESULT CDirectVoiceClientEngine::MigrateHost_RunElection( )
  3298. {
  3299. HRESULT hr;
  3300. if( m_dwCurrentState == DVCSTATE_DISCONNECTING )
  3301. {
  3302. DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: We're disconnecting, no need to run algorithm" );
  3303. return TRUE;
  3304. }
  3305. // This shortcut prevents problems if this is called twice.
  3306. // But only if we're lucky enough this got set before this poin
  3307. // We also have to guard against this case in HostMigrateCreate
  3308. //
  3309. //
  3310. if( m_lpdvServerMigrated != NULL )
  3311. {
  3312. DPFX(DPFPREP, DVF_INFOLEVEL, "Skipping calling removeplayer again as host already migrated" );
  3313. return DV_OK;
  3314. }
  3315. DWORD dwHostOrderID = DVPROTOCOL_HOSTORDER_INVALID;
  3316. DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Player 0x%x is running election algorithm", this->m_dvidLocal );
  3317. // Prevent double-create from host migration run. This can be called by removeplayer and by hostmigrateleave
  3318. // message.
  3319. CDVCSLock m_guardLock(&m_csClassLock);
  3320. m_guardLock.Lock();
  3321. // Trust me. Do this.
  3322. DVID dvidNewHost = m_dvidLocal;
  3323. // Check everyone else in the session, see who has lowest host order ID
  3324. dwHostOrderID = m_voiceNameTable.GetLowestHostOrderID(&dvidNewHost);
  3325. // We're lower then everyone else.. We should become new host
  3326. if( m_dwHostOrderID <= dwHostOrderID )
  3327. {
  3328. DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: We're to become the new host" );
  3329. dvidNewHost = m_dvidLocal;
  3330. }
  3331. DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: New Host is [0x%x] OrderID [0x%x] Current [0x%x]", dvidNewHost, dwHostOrderID, m_dwMigrateHostOrderID );
  3332. if( m_dwMigrateHostOrderID != DVPROTOCOL_HOSTORDER_INVALID && dwHostOrderID <= m_dwMigrateHostOrderID )
  3333. {
  3334. DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: No need to run algorithm again -- in progress (0x%x)", dvidNewHost );
  3335. m_guardLock.Unlock();
  3336. return DV_OK;
  3337. }
  3338. m_dwMigrateHostOrderID = dwHostOrderID;
  3339. m_lpSessionTransport->MigrateHost( dvidNewHost );
  3340. m_dvidServer = m_lpSessionTransport->GetServerID();
  3341. DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Host is (2) [0x%x]", m_dvidServer );
  3342. m_guardLock.Unlock();
  3343. // No one was found and we're not properly connected yet
  3344. if( dwHostOrderID == DVPROTOCOL_HOSTORDER_INVALID && m_dwCurrentState != DVCSTATE_CONNECTED )
  3345. {
  3346. DPFX(DPFPREP, DVF_ERRORLEVEL, "There is no one to take over session. Disconnect" );
  3347. // We're connecting.. expected behaviour
  3348. if( m_dwCurrentState == DVCSTATE_CONNECTING )
  3349. {
  3350. DPFX(DPFPREP, DVF_ERRORLEVEL, "Aborting connection..." );
  3351. SendConnectResult();
  3352. SetEvent( m_hConnectAck );
  3353. DoSessionLost(DVERR_SESSIONLOST);
  3354. }
  3355. // We're already disconnecting
  3356. else if( m_dwCurrentState == DVCSTATE_DISCONNECTING )
  3357. {
  3358. DPFX(DPFPREP, DVF_ERRORLEVEL, "Already disconnecting.." );
  3359. }
  3360. }
  3361. // Candidate was found, it's us!
  3362. else if( m_dwHostOrderID <= dwHostOrderID )
  3363. {
  3364. // Reset the player list received flag to -1 to allow another playerlist message
  3365. InterlockedExchange( &m_iPlayerListReceived, -1 );
  3366. DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: We think we're the host! Our Host Order ID=0x%x", m_dwHostOrderID );
  3367. hr = HandleLocalHostMigrateCreate();
  3368. if( FAILED( hr ) )
  3369. {
  3370. DPFX(DPFPREP, DVF_ERRORLEVEL, "Host migrate failed hr=0x%x", hr );
  3371. }
  3372. }
  3373. // Someone was elected -- not us!
  3374. //
  3375. // We send a settings confirm message in case we ignored host migration message
  3376. // during our election. (Small, but reproducable window).
  3377. //
  3378. // If we do get it after this host may get > 1 settings confirm from us (which is handled).
  3379. //
  3380. else if( dwHostOrderID != DVPROTOCOL_HOSTORDER_INVALID )
  3381. {
  3382. // Reset the player list received flag to -1 to allow another playerlist message
  3383. InterlockedExchange( &m_iPlayerListReceived, -1 );
  3384. hr = Send_SettingsConfirm();
  3385. if( FAILED( hr ) )
  3386. {
  3387. DPFX(DPFPREP, DVF_ERRORLEVEL, "Settings confirm message failed! sent hr=0x%x", hr );
  3388. }
  3389. DVMSG_HOSTMIGRATED dvHostMigrated;
  3390. dvHostMigrated.dvidNewHostID = m_dvidServer;
  3391. dvHostMigrated.pdvServerInterface = NULL;
  3392. dvHostMigrated.dwSize = sizeof( DVMSG_HOSTMIGRATED );
  3393. DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Sending notification because of election" );
  3394. NotifyQueue_Add( DVMSGID_HOSTMIGRATED, &dvHostMigrated, sizeof( DVMSG_HOSTMIGRATED ) );
  3395. }
  3396. return DV_OK;
  3397. }
  3398. #undef DPF_MODNAME
  3399. #define DPF_MODNAME "CDirectVoiceClientEngine::HandleDeleteVoicePlayer"
  3400. //
  3401. // HandleDeleteVoicePlayer
  3402. //
  3403. // Handles the DVMSGID_DELETEVOICEPLAYER message.
  3404. //
  3405. BOOL CDirectVoiceClientEngine::HandleDeleteVoicePlayer( DVID dvidSource, PDVPROTOCOLMSG_PLAYERQUIT lpdvDeletePlayer, DWORD dwSize )
  3406. {
  3407. CVoicePlayer *pPlayer;
  3408. HRESULT hr;
  3409. if ( dwSize != sizeof( DVPROTOCOLMSG_PLAYERQUIT ) )
  3410. {
  3411. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleDeleteVoicePlayer() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
  3412. dwSize, dvidSource );
  3413. return FALSE;
  3414. }
  3415. hr = m_voiceNameTable.GetEntry( lpdvDeletePlayer->dvidID, &pPlayer, TRUE );
  3416. // If there is a player entry for the given ID,
  3417. // Handle removing them from the local player table
  3418. if( pPlayer != NULL )
  3419. {
  3420. ASSERT_VPLAYER(pPlayer);
  3421. // Remove the entry, this will also drop the reference count
  3422. hr = m_voiceNameTable.DeleteEntry( lpdvDeletePlayer->dvidID );
  3423. // Another thread has already remove the player -- we don't need to do the rest
  3424. // of this.
  3425. if( FAILED( hr ) )
  3426. {
  3427. DPFX(DPFPREP, 0, "Error, could not find entry 0x%x to delete hr=0x%x", dvidSource, hr );
  3428. pPlayer->Release();
  3429. return TRUE;
  3430. }
  3431. // Mark player record as disconnected
  3432. pPlayer->SetDisconnected();
  3433. // Wait for global object lock and then remove target
  3434. // CDVCSLock guardLock(&m_csClassLock);
  3435. // guardLock.Lock();
  3436. CheckForAndRemoveTarget( lpdvDeletePlayer->dvidID );
  3437. // guardLock.Unlock();
  3438. // If there are any buffers for this player, delete them
  3439. // We don't need to destroy them, we want to save them so the user can call
  3440. // Delete3DUserBuffer
  3441. //
  3442. //DeleteSoundTarget( lpdvDeletePlayer->dvidID );
  3443. if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_PEER )
  3444. {
  3445. DVMSG_DELETEVOICEPLAYER dvMsgDeletePlayer;
  3446. // Event for doing sync wait
  3447. HANDLE hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  3448. dvMsgDeletePlayer.dvidPlayer = lpdvDeletePlayer->dvidID;
  3449. dvMsgDeletePlayer.dwSize = sizeof( DVMSG_DELETEVOICEPLAYER );
  3450. dvMsgDeletePlayer.pvPlayerContext = pPlayer->GetContext();
  3451. pPlayer->SetContext( NULL );
  3452. // By making this synchronous we ensure that the voice notification has completed before the dplay8
  3453. // callback is called.
  3454. //
  3455. NotifyQueue_Add( DVMSGID_DELETEVOICEPLAYER, &dvMsgDeletePlayer, sizeof( DVMSG_DELETEVOICEPLAYER ), &hEvent, NotifyComplete_SyncWait );
  3456. if( hEvent )
  3457. {
  3458. WaitForSingleObject( hEvent, INFINITE );
  3459. CloseHandle( hEvent );
  3460. }
  3461. }
  3462. pPlayer->Release();
  3463. }
  3464. return TRUE;
  3465. }
  3466. #undef DPF_MODNAME
  3467. #define DPF_MODNAME "CDirectVoiceClientEngine::QueueSpeech"
  3468. //
  3469. // QueueSpeech
  3470. //
  3471. // Process and queue incoming audio
  3472. //
  3473. BOOL CDirectVoiceClientEngine::QueueSpeech( DVID dvidSource, PDVPROTOCOLMSG_SPEECHHEADER pdvSpeechHeader, PBYTE pbData, DWORD dwSize )
  3474. {
  3475. CVoicePlayer *pPlayerInfo;
  3476. HRESULT hr;
  3477. // Only start receiving voice if the local player is active
  3478. if( !m_fLocalPlayerAvailable )
  3479. {
  3480. DPFX(DPFPREP, 1, "Ignoring incoming audio, local player has not been indicated" );
  3481. return TRUE;
  3482. }
  3483. hr = m_voiceNameTable.GetEntry( dvidSource, &pPlayerInfo, TRUE );
  3484. if( FAILED( hr ) )
  3485. {
  3486. DPFX(DPFPREP, 1, "Received speech for player who is not in nametable hr=0x%x", hr );
  3487. return TRUE;
  3488. }
  3489. ASSERT_VPLAYER(pPlayerInfo);
  3490. if( !pPlayerInfo->IsAvailable() )
  3491. {
  3492. DPFX(DPFPREP, 1, "Player is not yet available, ignoring speech" );
  3493. }
  3494. else
  3495. {
  3496. hr = pPlayerInfo->HandleReceive( pdvSpeechHeader, pbData, dwSize );
  3497. if( FAILED( hr ) )
  3498. {
  3499. pPlayerInfo->Release();
  3500. DPFX(DPFPREP, 1, "Received speech could not be buffered hr=0x%x", hr );
  3501. return TRUE;
  3502. }
  3503. // STATSBLOCK: Begin
  3504. m_pStatsBlob->m_dwPRESpeech++;
  3505. // STATSBLOCK: End
  3506. }
  3507. // Release our reference to the player
  3508. pPlayerInfo->Release();
  3509. return TRUE;
  3510. }
  3511. #undef DPF_MODNAME
  3512. #define DPF_MODNAME "CDirectVoiceClientEngine::HandleSpeechWithFrom"
  3513. //
  3514. // HandleSpeech
  3515. //
  3516. // Handles speech data messages
  3517. //
  3518. BOOL CDirectVoiceClientEngine::HandleSpeechWithFrom( DVID dvidSource, PDVPROTOCOLMSG_SPEECHWITHFROM lpdvSpeech, DWORD dwSize )
  3519. {
  3520. HRESULT hr;
  3521. // Only valid in forwarding sessions
  3522. if( m_dvSessionDesc.dwSessionType != DVSESSIONTYPE_FORWARDING )
  3523. {
  3524. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSpeechWithFrom() Ignoring fwd speech message in non-fwd session - from=0x%x", dvidSource );
  3525. return FALSE;
  3526. }
  3527. // This messages can only come from the server.
  3528. if( dvidSource != m_dvidServer )
  3529. {
  3530. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSpeechWithFrom() Ignoring fwd speech message from non-host - from=0x%x", dvidSource );
  3531. return FALSE;
  3532. }
  3533. if ( dwSize < sizeof( DVPROTOCOLMSG_SPEECHWITHFROM ) )
  3534. {
  3535. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSpeechWithFrom() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
  3536. dwSize, dvidSource );
  3537. return FALSE;
  3538. }
  3539. if( !ValidateSpeechPacketSize( m_lpdvfCompressionInfo, dwSize - sizeof( DVPROTOCOLMSG_SPEECHWITHFROM ) ) )
  3540. {
  3541. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSpeechWithFrom() Ignoring message with invalid speech size, size=0x%x, from=0x%x",
  3542. dwSize, dvidSource );
  3543. return FALSE;
  3544. }
  3545. if( !ValidatePlayerDVID( lpdvSpeech->dvidFrom ) )
  3546. {
  3547. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSpeechWithFrom() Ignoring message with invalid player dvid, flags=0x%x, from=0x%x",
  3548. lpdvSpeech->dvidFrom, dvidSource );
  3549. return FALSE;
  3550. }
  3551. DPFX(DPFPREP, DVF_INFOLEVEL, "Received multicast speech!" );
  3552. if( lpdvSpeech->dvidFrom == m_dvidLocal )
  3553. {
  3554. DPFX(DPFPREP, DVF_INFOLEVEL, "Ignoring loopback speech!" );
  3555. return TRUE;
  3556. }
  3557. CVoicePlayer *pPlayerInfo;
  3558. hr = m_voiceNameTable.GetEntry( lpdvSpeech->dvidFrom, &pPlayerInfo, TRUE );
  3559. if( FAILED( hr ) )
  3560. {
  3561. DVPROTOCOLMSG_PLAYERJOIN dvPlayerJoin;
  3562. dvPlayerJoin.dwFlags = 0;
  3563. dvPlayerJoin.dvidID = lpdvSpeech->dvidFrom;
  3564. // Call internal create voice player handler -- we've done all validation by now.
  3565. InternalCreateVoicePlayer( lpdvSpeech->dvidFrom, &dvPlayerJoin, sizeof( DVPROTOCOLMSG_PLAYERJOIN ) );
  3566. }
  3567. else
  3568. {
  3569. ASSERT_VPLAYER(pPlayerInfo);
  3570. pPlayerInfo->Release();
  3571. }
  3572. return QueueSpeech( lpdvSpeech->dvidFrom, &lpdvSpeech->dvHeader, (PBYTE) &lpdvSpeech[1], dwSize-sizeof(DVPROTOCOLMSG_SPEECHWITHFROM) );
  3573. }
  3574. #undef DPF_MODNAME
  3575. #define DPF_MODNAME "CDirectVoiceClientEngine::HandleSpeechBounce"
  3576. //
  3577. // HandleSpeech
  3578. //
  3579. // Handles speech data messages
  3580. //
  3581. BOOL CDirectVoiceClientEngine::HandleSpeechBounce( DVID dvidSource, PDVPROTOCOLMSG_SPEECHHEADER lpdvSpeech, DWORD dwSize )
  3582. {
  3583. if ( dwSize < sizeof( DVPROTOCOLMSG_SPEECHHEADER ) )
  3584. {
  3585. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSpeechBounce() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
  3586. dwSize, dvidSource );
  3587. return FALSE;
  3588. }
  3589. if( !ValidateSpeechPacketSize( m_lpdvfCompressionInfo, dwSize - sizeof( DVPROTOCOLMSG_SPEECHHEADER ) ) )
  3590. {
  3591. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSpeechBounce() Ignoring message with invalid speech size, size=0x%x, from=0x%x",
  3592. dwSize, dvidSource );
  3593. return FALSE;
  3594. }
  3595. DPFX(DPFPREP, DVF_INFOLEVEL, "Received speech bounce!" );
  3596. return QueueSpeech( dvidSource, lpdvSpeech, (PBYTE) &lpdvSpeech[1], dwSize - sizeof( DVPROTOCOLMSG_SPEECHHEADER ) );
  3597. }
  3598. #undef DPF_MODNAME
  3599. #define DPF_MODNAME "CDirectVoiceClientEngine::HandleSpeech"
  3600. //
  3601. // HandleSpeech
  3602. //
  3603. // Handles speech data messages
  3604. //
  3605. BOOL CDirectVoiceClientEngine::HandleSpeech( DVID dvidSource, PDVPROTOCOLMSG_SPEECHHEADER lpdvSpeech, DWORD dwSize )
  3606. {
  3607. if ( dwSize < sizeof( DVPROTOCOLMSG_SPEECHHEADER ) )
  3608. {
  3609. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSpeech() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
  3610. dwSize, dvidSource );
  3611. return FALSE;
  3612. }
  3613. if( !ValidateSpeechPacketSize( m_lpdvfCompressionInfo, dwSize - sizeof( DVPROTOCOLMSG_SPEECHHEADER ) ) )
  3614. {
  3615. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSpeech() Ignoring message with invalid speech size, size=0x%x, from=0x%x",
  3616. dwSize, dvidSource );
  3617. return FALSE;
  3618. }
  3619. DPFX(DPFPREP, DVF_INFOLEVEL, "Received bare speech!" );
  3620. // Ignore speech from ourselves
  3621. if( dvidSource == m_dvidLocal )
  3622. {
  3623. DPFX(DPFPREP, DVF_INFOLEVEL, "Ignoring loopback speech!" );
  3624. return TRUE;
  3625. }
  3626. return QueueSpeech( dvidSource, lpdvSpeech, (PBYTE) &lpdvSpeech[1], dwSize - sizeof( DVPROTOCOLMSG_SPEECHHEADER ) );
  3627. }
  3628. #undef DPF_MODNAME
  3629. #define DPF_MODNAME "CDirectVoiceClientEngine::Cleanup"
  3630. //
  3631. // Cleanup
  3632. //
  3633. // WARNING: Do not call this function multiple times on the same object.
  3634. //
  3635. // This function shuts down the recording and playback threads, shuts down
  3636. // the sound system and unhooks the object from the dplay object.
  3637. //
  3638. // Called By:
  3639. // - DoDisconnect
  3640. // - Destructor
  3641. // - HandleConnectAccept
  3642. // - NotifyThread
  3643. //
  3644. // Locks Required:
  3645. // - Global Write Lock
  3646. //
  3647. void CDirectVoiceClientEngine::Cleanup()
  3648. {
  3649. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Cleanup called!" );
  3650. // Enter cleanup critical section. Only one instance should be in here at a time.
  3651. DNEnterCriticalSection( &m_csCleanupProtect );
  3652. CDVCSLock guardLock(&m_csClassLock);
  3653. guardLock.Lock();
  3654. // We only need to cleanup if we're not idle
  3655. if( m_dwCurrentState == DVCSTATE_IDLE )
  3656. {
  3657. DNLeaveCriticalSection( &m_csCleanupProtect );
  3658. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Cleanup not required" );
  3659. return;
  3660. }
  3661. if( m_hRecordThreadHandle )
  3662. {
  3663. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Signalling record to terminate" );
  3664. // Signal record thread to shutdown
  3665. SetEvent( m_hRecordTerminate );
  3666. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Record signalled" );
  3667. // Release write lock, if we don't we may have a deadlock
  3668. // because recordthread can't get dplay lock, which means
  3669. // can't shutdown
  3670. //
  3671. // Part of a three way deadlock case.
  3672. guardLock.Unlock();
  3673. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Waiting for record shutdown" );
  3674. WaitForSingleObject( m_hRecordDone, INFINITE );
  3675. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Record shutdown complete" );
  3676. // Re-acquire lock, we need it.
  3677. guardLock.Lock();
  3678. m_hRecordThreadHandle = NULL;
  3679. }
  3680. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Record cleanup" );
  3681. if( m_hRecordTerminate )
  3682. {
  3683. CloseHandle( m_hRecordTerminate );
  3684. m_hRecordTerminate = NULL;
  3685. }
  3686. if( m_hRecordDone )
  3687. {
  3688. CloseHandle( m_hRecordDone );
  3689. m_hRecordDone = NULL;
  3690. }
  3691. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Record cleanup complete" );
  3692. if( m_hPlaybackThreadHandle != NULL )
  3693. {
  3694. // Signal playback thread to shutdown
  3695. SetEvent( m_hPlaybackTerminate );
  3696. // Release write lock to prevent deadlock.
  3697. //
  3698. guardLock.Unlock();
  3699. WaitForSingleObject( m_hPlaybackDone, INFINITE );
  3700. // Re-acquire lock, we need it.
  3701. guardLock.Lock();
  3702. m_hPlaybackThreadHandle = NULL;
  3703. }
  3704. if( m_hPlaybackTerminate )
  3705. {
  3706. CloseHandle( m_hPlaybackTerminate );
  3707. m_hPlaybackTerminate = NULL;
  3708. }
  3709. if( m_hPlaybackDone )
  3710. {
  3711. CloseHandle( m_hPlaybackDone );
  3712. m_hPlaybackDone = NULL;
  3713. }
  3714. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Playback Thread Done" );
  3715. ClientStats_End();
  3716. DPFX(DPFPREP, DVF_INFOLEVEL, "Threads gone!" );
  3717. // If we're running in client/server we need destroy the server
  3718. // buffer.
  3719. if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_MIXING ||
  3720. m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_ECHO )
  3721. {
  3722. DeInitializeClientServer();
  3723. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Client/Server Gone" );
  3724. }
  3725. guardLock.Unlock();
  3726. // Disable notifications, no notifications after this point can be made
  3727. NotifyQueue_Disable();
  3728. // The following code is extremely sensitive.
  3729. //
  3730. // Be careful about the order here, it's important.
  3731. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Waiting for outstanding sends" );
  3732. // Wait for outstanding buffer sends to complete before
  3733. // continuing. Otherwise you have potential crash / leak
  3734. // condition
  3735. WaitForBufferReturns();
  3736. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Disconnecting transport" );
  3737. // After this function returns DirectPlay will no longer sends
  3738. // us indication, but some may still be in progress.
  3739. m_lpSessionTransport->DisableReceiveHook( );
  3740. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Flushing notification queue" );
  3741. // Ensuring that all notifications have been sent
  3742. NotifyQueue_Flush();
  3743. // Waiting for transport to return all the threads it is indicating into us on.
  3744. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Waiting for transport threads to complete" );
  3745. m_lpSessionTransport->WaitForDetachCompletion();
  3746. // Re-enable notifications
  3747. NotifyQueue_Enable();
  3748. guardLock.Lock();
  3749. if( m_pTimer != NULL )
  3750. {
  3751. delete m_pTimer;
  3752. m_pTimer = NULL;
  3753. }
  3754. if( m_thTimerInfo.hPlaybackTimerEvent != NULL )
  3755. {
  3756. CloseHandle( m_thTimerInfo.hPlaybackTimerEvent );
  3757. m_thTimerInfo.hPlaybackTimerEvent = NULL;
  3758. }
  3759. if( m_thTimerInfo.hRecordTimerEvent != NULL )
  3760. {
  3761. CloseHandle( m_thTimerInfo.hRecordTimerEvent );
  3762. m_thTimerInfo.hRecordTimerEvent = NULL;
  3763. }
  3764. CleanupPlaybackLists();
  3765. CleanupNotifyLists();
  3766. // Inform player of their own exit if they were connected!
  3767. if( m_fLocalPlayerNotify )
  3768. {
  3769. DVMSG_DELETEVOICEPLAYER dvMsgDelete;
  3770. dvMsgDelete.dvidPlayer = m_dvidLocal;
  3771. dvMsgDelete.dwSize = sizeof( DVMSG_DELETEVOICEPLAYER );
  3772. dvMsgDelete.pvPlayerContext = m_pvLocalPlayerContext;
  3773. m_pvLocalPlayerContext = NULL;
  3774. m_fLocalPlayerNotify = FALSE;
  3775. m_fLocalPlayerAvailable = FALSE;
  3776. TransmitMessage( DVMSGID_DELETEVOICEPLAYER, &dvMsgDelete, sizeof( DVMSG_DELETEVOICEPLAYER ) );
  3777. }
  3778. m_voiceNameTable.DeInitialize( (m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_PEER), m_lpUserContext, m_lpMessageHandler);
  3779. // Hold off on the shutdown of the sound system so user notifications
  3780. // of delete players on unravel of nametable can be handled correctly.
  3781. //
  3782. ShutdownSoundSystem();
  3783. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Sound system shutdown" );
  3784. m_fpPlayers.DeInitialize();
  3785. FreeBuffers();
  3786. if( m_pFramePool != NULL )
  3787. {
  3788. DNEnterCriticalSection( &CDirectVoiceEngine::s_csSTLLock );
  3789. delete m_pFramePool;
  3790. m_pFramePool = NULL;
  3791. DNLeaveCriticalSection( &CDirectVoiceEngine::s_csSTLLock );
  3792. }
  3793. SetCurrentState( DVCSTATE_IDLE );
  3794. DNEnterCriticalSection( &m_csTargetLock );
  3795. if( m_pdvidTargets != NULL )
  3796. {
  3797. delete [] m_pdvidTargets;
  3798. m_pdvidTargets = NULL;
  3799. m_dwNumTargets = 0;
  3800. m_dwTargetVersion = 0;
  3801. }
  3802. DNLeaveCriticalSection( &m_csTargetLock );
  3803. DNLeaveCriticalSection( &m_csCleanupProtect );
  3804. ClientStats_Dump();
  3805. m_pStatsBlob = NULL;
  3806. SetEvent( m_hDisconnectAck );
  3807. guardLock.Unlock();
  3808. if( m_lpdvServerMigrated != NULL )
  3809. {
  3810. m_lpdvServerMigrated->Release();
  3811. m_lpdvServerMigrated = NULL;
  3812. }
  3813. }
  3814. // WaitForBufferReturns
  3815. //
  3816. // This function waits until oustanding sends have completed before continuing
  3817. // we use this to ensure we don't deregister with outstanding sends.
  3818. //
  3819. #undef DPF_MODNAME
  3820. #define DPF_MODNAME "CDirectVoiceClientEngine::WaitForBufferReturns"
  3821. void CDirectVoiceClientEngine::WaitForBufferReturns()
  3822. {
  3823. while( 1 )
  3824. {
  3825. if( m_BufferDescPool.GetInUseCount() == 0 )
  3826. {
  3827. break;
  3828. }
  3829. Sleep( 20 );
  3830. }
  3831. return;
  3832. }
  3833. #undef DPF_MODNAME
  3834. #define DPF_MODNAME "CDirectVoiceClientEngine::DoDisconnect"
  3835. //
  3836. // DoDisconnect
  3837. //
  3838. // Performs a disconnection and informs the callback function.
  3839. //
  3840. // Used for both session lost and normal disconnects.
  3841. //
  3842. // Called By:
  3843. // - NotifyThread
  3844. //
  3845. void CDirectVoiceClientEngine::DoDisconnect()
  3846. {
  3847. // Guard to prevent this function from being called more then once on the
  3848. // same object
  3849. if( m_dwCurrentState == DVCSTATE_IDLE ||
  3850. m_dwCurrentState == DVCSTATE_NOTINITIALIZED )
  3851. return;
  3852. // This codeblock ensures that this path will only be run once per connection. Protects against
  3853. // multiple disconnect confirm messages sent to the client.
  3854. DNEnterCriticalSection( &m_csCleanupProtect );
  3855. if( m_fDisconnecting )
  3856. {
  3857. DNLeaveCriticalSection( &m_csCleanupProtect );
  3858. return;
  3859. }
  3860. m_fDisconnecting = TRUE;
  3861. DNLeaveCriticalSection( &m_csCleanupProtect );
  3862. m_dwCurrentState = DVCSTATE_DISCONNECTING;
  3863. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "DoDisconnect called!" );
  3864. Cleanup();
  3865. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Cleanup complete" );
  3866. if( m_fSessionLost )
  3867. {
  3868. DVMSG_SESSIONLOST dvSessionLost;
  3869. dvSessionLost.hrResult = m_hrDisconnectResult;
  3870. dvSessionLost.dwSize = sizeof( DVMSG_SESSIONLOST );
  3871. NotifyQueue_Add( DVMSGID_SESSIONLOST, &dvSessionLost, sizeof( DVMSG_SESSIONLOST ) );
  3872. }
  3873. else
  3874. {
  3875. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Signalling disconnect result" );
  3876. SendDisconnectResult();
  3877. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Done signalling disconnect" );
  3878. }
  3879. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Disconnect complete" );
  3880. }
  3881. #undef DPF_MODNAME
  3882. #define DPF_MODNAME "CDirectVoiceClientEngine::HandleDisconnect"
  3883. // HandleDisconnect
  3884. //
  3885. // This function is called when a disconnect message is received from the
  3886. // server.
  3887. //
  3888. BOOL CDirectVoiceClientEngine::HandleDisconnectConfirm( DVID dvidSource, PDVPROTOCOLMSG_DISCONNECT lpdvDisconnect, DWORD dwSize )
  3889. {
  3890. if ( dwSize != sizeof( DVPROTOCOLMSG_DISCONNECT ) )
  3891. {
  3892. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleDisconnectConfirm() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
  3893. dwSize, dvidSource );
  3894. return FALSE;
  3895. }
  3896. // Ignore disconnect message from someone who is not the server
  3897. if( dvidSource != m_dvidServer )
  3898. {
  3899. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleDisconnectConfirm() Ignoring disconnect from non-server" );
  3900. return FALSE;
  3901. }
  3902. // We're not in the disconnecting state, so this message is irrelevant
  3903. if( m_dwCurrentState != DVCSTATE_DISCONNECTING )
  3904. {
  3905. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleDisconnectConfirm() Ignoring disconnect as we're not disconnecting!" );
  3906. return FALSE;
  3907. }
  3908. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "DisconnectConfirm received, signalling worker [res=0x%x]", lpdvDisconnect->hresDisconnect );
  3909. DoSignalDisconnect( lpdvDisconnect->hresDisconnect );
  3910. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "DisconnectConfirm received, signalled worker" );
  3911. return TRUE;
  3912. }
  3913. #undef DPF_MODNAME
  3914. #define DPF_MODNAME "CDirectVoiceClientEngine::HandleSessionLost"
  3915. BOOL CDirectVoiceClientEngine::HandleSessionLost( DVID dvidSource, PDVPROTOCOLMSG_SESSIONLOST lpdvSessionLost, DWORD dwSize )
  3916. {
  3917. // This messages can only come from the server.
  3918. if( dvidSource != m_dvidServer )
  3919. {
  3920. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "Ignoring session lost message from non-host - from=0x%x", dvidSource );
  3921. return FALSE;
  3922. }
  3923. if ( dwSize != sizeof( DVPROTOCOLMSG_SESSIONLOST ) )
  3924. {
  3925. DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVCE::HandleSessionLost() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
  3926. dwSize, dvidSource );
  3927. return FALSE;
  3928. }
  3929. DPFX(DPFPREP, DVF_ENTRYLEVEL, "DVCE::HandleSessionLost() begin" );
  3930. DPFX(DPFPREP, DVF_ERRORLEVEL, "<><><><><><><> Session Host has shutdown - Voice Session is gone." );
  3931. DoSessionLost( lpdvSessionLost->hresReason );
  3932. return TRUE;
  3933. }
  3934. #undef DPF_MODNAME
  3935. #define DPF_MODNAME "CDirectVoiceClientEngine::StartTransportSession"
  3936. HRESULT CDirectVoiceClientEngine::StartTransportSession( )
  3937. {
  3938. return S_OK;
  3939. }
  3940. #undef DPF_MODNAME
  3941. #define DPF_MODNAME "CDirectVoiceClientEngine::StopTransportSession"
  3942. // StopSession
  3943. //
  3944. // This function is called when the directplay session is lost or stops
  3945. // before DirectXVoice is disconnected.
  3946. //
  3947. HRESULT CDirectVoiceClientEngine::StopTransportSession()
  3948. {
  3949. DoSessionLost( DVERR_SESSIONLOST );
  3950. return DV_OK;
  3951. }
  3952. #undef DPF_MODNAME
  3953. #define DPF_MODNAME "CDirectVoiceClientEngine::AddPlayer"
  3954. HRESULT CDirectVoiceClientEngine::AddPlayer( DVID dvID )
  3955. {
  3956. return S_OK;
  3957. }
  3958. #undef DPF_MODNAME
  3959. #define DPF_MODNAME "CDirectVoiceClientEngine::HandleLocalHostMigrateCreate"
  3960. HRESULT CDirectVoiceClientEngine::HandleLocalHostMigrateCreate()
  3961. {
  3962. LPDIRECTVOICESERVEROBJECT lpdvsServerObject = NULL;
  3963. DWORD dwSessionSize = 0;
  3964. HRESULT hr = DP_OK;
  3965. CDirectVoiceDirectXTransport *pTransport;
  3966. // Prevent double-create from host migration run.
  3967. CDVCSLock m_guardLock(&m_csClassLock);
  3968. m_guardLock.Lock();
  3969. if( m_lpdvServerMigrated )
  3970. {
  3971. DPFX(DPFPREP, DVF_WARNINGLEVEL, "Duplicate host create received. Ignoring" );
  3972. return DV_OK;
  3973. }
  3974. DPFX(DPFPREP, DVF_ERRORLEVEL, "Local client has become the new host. Creating a host" );
  3975. hr = DVS_Create( &lpdvsServerObject );
  3976. if( FAILED( hr ) )
  3977. {
  3978. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to create server object. hr=0x%x", hr );
  3979. goto HOSTCREATE_FAILURE;
  3980. }
  3981. // Grab a reference local of the new host interface
  3982. m_lpdvServerMigrated = (LPDIRECTPLAYVOICESERVER) lpdvsServerObject;
  3983. m_guardLock.Unlock();
  3984. IncrementObjectCount();
  3985. lpdvsServerObject->lIntRefCnt++;
  3986. DVMSG_LOCALHOSTSETUP dvMsgLocalHostSetup;
  3987. dvMsgLocalHostSetup.dwSize = sizeof( DVMSG_LOCALHOSTSETUP );
  3988. dvMsgLocalHostSetup.pvContext = NULL;
  3989. dvMsgLocalHostSetup.pMessageHandler = NULL;
  3990. TransmitMessage( DVMSGID_LOCALHOSTSETUP, &dvMsgLocalHostSetup, sizeof( DVMSG_LOCALHOSTSETUP ) );
  3991. pTransport = (CDirectVoiceDirectXTransport *) m_lpSessionTransport;
  3992. hr = DV_Initialize( lpdvsServerObject, pTransport->GetTransportInterface(), dvMsgLocalHostSetup.pMessageHandler, dvMsgLocalHostSetup.pvContext, NULL, 0 );
  3993. if( FAILED( hr ) )
  3994. {
  3995. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to initialize the server object hr=0x%x", hr );
  3996. goto HOSTCREATE_FAILURE;
  3997. }
  3998. hr = lpdvsServerObject->lpDVServerEngine->HostMigrateStart( &m_dvSessionDesc, m_dwHostOrderID+DVMIGRATE_ORDERID_OFFSET );
  3999. if( FAILED( hr ) )
  4000. {
  4001. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error starting server object hr=0x%x", hr );
  4002. goto HOSTCREATE_FAILURE;
  4003. }
  4004. DVMSG_HOSTMIGRATED dvHostMigrated;
  4005. dvHostMigrated.dvidNewHostID = m_lpSessionTransport->GetLocalID();
  4006. dvHostMigrated.pdvServerInterface = (LPDIRECTPLAYVOICESERVER) lpdvsServerObject;
  4007. dvHostMigrated.dwSize = sizeof( DVMSG_HOSTMIGRATED );
  4008. NotifyQueue_Add( DVMSGID_HOSTMIGRATED, &dvHostMigrated, sizeof( DVMSG_HOSTMIGRATED ) );
  4009. return DV_OK;
  4010. HOSTCREATE_FAILURE:
  4011. DPFX(DPFPREP, DVF_ERRORLEVEL, "Informing clients of our failure to create host" );
  4012. Send_SessionLost();
  4013. if( lpdvsServerObject != NULL )
  4014. {
  4015. DVS_Release( lpdvsServerObject );
  4016. }
  4017. return hr;
  4018. }
  4019. // Handles remove player message
  4020. //
  4021. // This message triggers handling of host migration if the player
  4022. // who has dropped out happens to be the session host.
  4023. //
  4024. #undef DPF_MODNAME
  4025. #define DPF_MODNAME "CDirectVoiceClientEngine::RemovePlayer"
  4026. HRESULT CDirectVoiceClientEngine::RemovePlayer( DVID dvID )
  4027. {
  4028. HRESULT hr;
  4029. CDVCSLock m_guardLock(&m_csClassLock);
  4030. m_guardLock.Lock();
  4031. if( m_dwCurrentState == DVCSTATE_DISCONNECTING )
  4032. {
  4033. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Ignoring transport disconnect for 0x%x -- client is disconnecting", dvID );
  4034. return DV_OK;
  4035. }
  4036. m_guardLock.Unlock();
  4037. DVPROTOCOLMSG_PLAYERQUIT dvPlayerQuit;
  4038. dvPlayerQuit.dwType = DVMSGID_DELETEVOICEPLAYER;
  4039. dvPlayerQuit.dvidID = dvID;
  4040. HandleDeleteVoicePlayer( 0, &dvPlayerQuit, sizeof( DVPROTOCOLMSG_PLAYERQUIT ) );
  4041. if( dvID == m_lpSessionTransport->GetServerID() )
  4042. {
  4043. // Don't run host migration code unless it is appropriate
  4044. // We could simplify this, but instead using existing code.
  4045. DWORD dwTransportSessionType, dwTransportFlags;
  4046. hr = m_lpSessionTransport->GetTransportSettings( &dwTransportSessionType, &dwTransportFlags );
  4047. if( FAILED( hr ) )
  4048. {
  4049. DNASSERT( FALSE );
  4050. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to retrieve transport settings hr=0x%x", hr );
  4051. return DV_OK;
  4052. }
  4053. if( (m_dvSessionDesc.dwSessionType != DVSESSIONTYPE_PEER ) ||
  4054. (m_dvSessionDesc.dwFlags & DVSESSION_NOHOSTMIGRATION) ||
  4055. !(dwTransportFlags & DVTRANSPORT_MIGRATEHOST) )
  4056. {
  4057. DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Host migration is disabled." );
  4058. DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Host is gone. Session will exit soon." );
  4059. if( m_dwCurrentState == DVCSTATE_CONNECTING )
  4060. {
  4061. DPFX(DPFPREP, DVF_ERRORLEVEL, "Aborting connection..." );
  4062. SendConnectResult();
  4063. SetEvent( m_hConnectAck );
  4064. }
  4065. DoSessionLost(DVERR_SESSIONLOST);
  4066. return DV_OK;
  4067. }
  4068. }
  4069. // The person who dropped out was the server
  4070. if( dvID == m_lpSessionTransport->GetServerID() )
  4071. {
  4072. DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Checking to see if remove of 0x%x is host 0x%x", dvID, m_lpSessionTransport->GetServerID() );
  4073. DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Triggered by Remove Player Message" );
  4074. MigrateHost_RunElection();
  4075. }
  4076. return S_OK;
  4077. }
  4078. #undef DPF_MODNAME
  4079. #define DPF_MODNAME "CDirectVoiceClientEngine::SetCurrentState"
  4080. // SetCurrentState
  4081. //
  4082. // Sets the current state of the client engine
  4083. //
  4084. void CDirectVoiceClientEngine::SetCurrentState( DWORD dwState )
  4085. {
  4086. CDVCSLock m_guardLock(&m_csClassLock);
  4087. m_guardLock.Lock();
  4088. m_dwCurrentState = dwState;
  4089. m_guardLock.Unlock();
  4090. }
  4091. #undef DPF_MODNAME
  4092. #define DPF_MODNAME "CDirectVoiceClientEngine::CheckShouldSendMessage"
  4093. //
  4094. // CheckShouldSendMessage
  4095. //
  4096. // Checks the notification mask to see if the specified message type should
  4097. // be sent to the user.
  4098. //
  4099. BOOL CDirectVoiceClientEngine::CheckShouldSendMessage( DWORD dwMessageType )
  4100. {
  4101. if( m_lpMessageHandler == NULL )
  4102. {
  4103. return FALSE;
  4104. }
  4105. BFCSingleLock slLock( &m_csNotifyLock );
  4106. slLock.Lock();
  4107. BOOL fSend = FALSE;
  4108. if( m_dwNumMessageElements == 0 )
  4109. {
  4110. return TRUE;
  4111. }
  4112. else
  4113. {
  4114. for( DWORD dwIndex = 0; dwIndex < m_dwNumMessageElements; dwIndex++ )
  4115. {
  4116. if( m_lpdwMessageElements[dwIndex] == dwMessageType )
  4117. {
  4118. return TRUE;
  4119. }
  4120. }
  4121. }
  4122. return FALSE;
  4123. }
  4124. #undef DPF_MODNAME
  4125. #define DPF_MODNAME "CDirectVoiceClientEngine::TransmitMessage"
  4126. //
  4127. // TransmitMessage
  4128. //
  4129. // Called to send a notification to the user.
  4130. //
  4131. // Only the notify thread should call this function, all other threads should queue up
  4132. // notifications by calling NotifyQueue_Add.
  4133. //
  4134. // Called By:
  4135. // - NotifyThread.
  4136. //
  4137. void CDirectVoiceClientEngine::TransmitMessage( DWORD dwMessageType, LPVOID lpData, DWORD dwSize )
  4138. {
  4139. if( CheckShouldSendMessage( dwMessageType ) )
  4140. {
  4141. (*m_lpMessageHandler)( m_lpUserContext, dwMessageType, (!dwSize) ? NULL : lpData );
  4142. }
  4143. }
  4144. #undef DPF_MODNAME
  4145. #define DPF_MODNAME "CDirectVoiceClientEngine::CheckForDuplicateObjects"
  4146. HRESULT CDirectVoiceClientEngine::CheckForDuplicateObjects()
  4147. {
  4148. HRESULT hr;
  4149. LPKSPROPERTYSET lpksPropSet = NULL;
  4150. DSPROPERTY_DIRECTSOUND_OBJECTS_DATA* pDSList = NULL;
  4151. DSPROPERTY_DIRECTSOUNDCAPTURE_OBJECTS_DATA* pDSCList = NULL;
  4152. DWORD dwIndex;
  4153. BOOL fFound;
  4154. hr = DirectSoundPrivateCreate( &lpksPropSet );
  4155. if( FAILED( hr ) )
  4156. {
  4157. DPFX(DPFPREP, DVF_WARNINGLEVEL, "Unable to check for usage of duplicate devices hr=0x%x", hr );
  4158. return DV_OK;
  4159. }
  4160. hr = PrvGetDirectSoundObjects( lpksPropSet, GUID_NULL, &pDSList );
  4161. if( FAILED( hr ) )
  4162. {
  4163. DPFX(DPFPREP, DVF_WARNINGLEVEL, "Unable to retrieve process sound objs hr=0x%x", hr );
  4164. hr = DV_OK;
  4165. }
  4166. // Check list for duplicate of the one we are using.
  4167. else
  4168. {
  4169. // User specified a device, be nice and print a debug message if the object doesn't
  4170. // match the GUID
  4171. if( m_dvSoundDeviceConfig.lpdsPlaybackDevice != NULL )
  4172. {
  4173. fFound = FALSE;
  4174. // Check the internal list
  4175. for( dwIndex = 0; dwIndex < pDSList->Count; dwIndex++ )
  4176. {
  4177. // Check to see if object user specified matches this one.
  4178. if( pDSList->Objects[dwIndex].DirectSound == m_dvSoundDeviceConfig.lpdsPlaybackDevice )
  4179. {
  4180. if( m_dvSoundDeviceConfig.guidPlaybackDevice != pDSList->Objects[dwIndex].DeviceId )
  4181. {
  4182. // Expected behaviour with emulated object
  4183. DPFX(DPFPREP, DVF_ERRORLEVEL, "Specified GUID is not correct for specified dsound object" );
  4184. fFound = FALSE;
  4185. }
  4186. else
  4187. {
  4188. fFound = TRUE;
  4189. DPFX(DPFPREP, DVF_INFOLEVEL, "GUID for device matches object. User param valid" );
  4190. }
  4191. break;
  4192. }
  4193. }
  4194. // We didn't find specified object in dsound's list.
  4195. // Could be an error condition, but would prevent emulated objects from working.
  4196. if( !fFound )
  4197. {
  4198. DPFX(DPFPREP, DVF_ERRORLEVEL, "Specified DirectSound object does not exist" );
  4199. }
  4200. }
  4201. // No object specified.
  4202. else
  4203. {
  4204. // Check the internal list to see if we need to make use of an existing object
  4205. for( dwIndex = 0; dwIndex < pDSList->Count; dwIndex++ )
  4206. {
  4207. // We have a winner, a matching playback device.
  4208. if( m_dvSoundDeviceConfig.guidPlaybackDevice == pDSList->Objects[dwIndex].DeviceId )
  4209. {
  4210. hr = pDSList->Objects[dwIndex].DirectSound->QueryInterface( IID_IDirectSound, (void **) &m_dvSoundDeviceConfig.lpdsPlaybackDevice );
  4211. if( FAILED( hr ) )
  4212. {
  4213. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to retrieve dsound int for existing dsound object hr=0x%x", hr );
  4214. hr = DVERR_INVALIDDEVICE;
  4215. goto EXIT_SOUND_CHECK;
  4216. }
  4217. break;
  4218. }
  4219. }
  4220. }
  4221. }
  4222. hr = PrvGetDirectSoundCaptureObjects( lpksPropSet, GUID_NULL, &pDSCList );
  4223. if( FAILED( hr ) )
  4224. {
  4225. DPFX(DPFPREP, DVF_WARNINGLEVEL, "Unable to retrieve process cap objs hr=0x%x", hr );
  4226. hr = DV_OK;
  4227. }
  4228. // Check list for duplicate of the one we are using
  4229. else
  4230. {
  4231. // User specified a device, be nice and print a debug message if the object doesn't
  4232. // match the GUID
  4233. if( m_dvSoundDeviceConfig.lpdsCaptureDevice != NULL )
  4234. {
  4235. fFound = FALSE;
  4236. // Check the internal list
  4237. for( dwIndex = 0; dwIndex < pDSCList->Count; dwIndex++ )
  4238. {
  4239. // Check to see if object user specified matches this one.
  4240. if( pDSCList->Objects[dwIndex].DirectSoundCapture == m_dvSoundDeviceConfig.lpdsCaptureDevice )
  4241. {
  4242. if( m_dvSoundDeviceConfig.guidCaptureDevice != pDSCList->Objects[dwIndex].DeviceId )
  4243. {
  4244. // Expected behaviour with emulated object
  4245. DPFX(DPFPREP, DVF_ERRORLEVEL, "Specified GUID is not correct for specified dsound object" );
  4246. fFound = FALSE;
  4247. }
  4248. else
  4249. {
  4250. fFound = TRUE;
  4251. DPFX(DPFPREP, DVF_INFOLEVEL, "GUID for device matches object. User param valid" );
  4252. }
  4253. break;
  4254. }
  4255. }
  4256. // We didn't find specified object in dsound's list.
  4257. // Could be an error condition, but would prevent emulated objects from working.
  4258. if( !fFound )
  4259. {
  4260. DPFX(DPFPREP, DVF_ERRORLEVEL, "Specified DirectSoundCap object does not exist" );
  4261. }
  4262. }
  4263. }
  4264. EXIT_SOUND_CHECK:
  4265. if( pDSList != NULL )
  4266. {
  4267. delete [] pDSList;
  4268. }
  4269. if( pDSCList != NULL )
  4270. {
  4271. delete [] pDSCList;
  4272. }
  4273. lpksPropSet->Release();
  4274. return hr;
  4275. }
  4276. #undef DPF_MODNAME
  4277. #define DPF_MODNAME "CDirectVoiceClientEngine::InitializeSoundSystem"
  4278. // InitializeSoundSystem
  4279. //
  4280. // Starts up the sound system based on the parameters.
  4281. //
  4282. HRESULT CDirectVoiceClientEngine::InitializeSoundSystem()
  4283. {
  4284. HRESULT hr;
  4285. Diagnostics_Begin( s_fDumpDiagnostics, "dpv_main.txt" );
  4286. DSERRTRACK_Reset();
  4287. Diagnostics_DeviceInfo( &m_dvSoundDeviceConfig.guidPlaybackDevice, &m_dvSoundDeviceConfig.guidCaptureDevice );
  4288. DPFX(DPFPREP, DVF_ENTRYLEVEL, "DVCE::InitializeSoundSystem() Begin" );
  4289. // Note: Mapping of default devices has already been performed.
  4290. // On Pre DX7.1 systems, all default devices map to GUID_NULL
  4291. // On DX7.1 and later, default device will have been mapped to their real GUIDs
  4292. //
  4293. m_dwCompressedFrameSize = m_lpdvfCompressionInfo->dwFrameLength;
  4294. m_dwUnCompressedFrameSize = DVCDB_CalcUnCompressedFrameSize( m_lpdvfCompressionInfo, s_lpwfxPlaybackFormat );
  4295. m_dwNumPerBuffer = m_lpdvfCompressionInfo->dwFramesPerBuffer;
  4296. // Setup the description for the main playback buffer
  4297. //
  4298. // Needs to be after above because it depends on proper values above
  4299. SetupPlaybackBufferDesc( &m_dsBufferDesc, NULL );
  4300. // If they gave us an object, just use it
  4301. if( m_dvSoundDeviceConfig.lpdsPlaybackDevice != NULL )
  4302. {
  4303. CDirectSoundPlaybackDevice *tmpDevice;
  4304. tmpDevice = new CDirectSoundPlaybackDevice( );
  4305. if( tmpDevice == NULL )
  4306. {
  4307. DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to alloc memory" );
  4308. return DVERR_OUTOFMEMORY;
  4309. }
  4310. hr = tmpDevice->Initialize( m_dvSoundDeviceConfig.lpdsPlaybackDevice, m_dvSoundDeviceConfig.guidPlaybackDevice );
  4311. if( FAILED( hr ) )
  4312. {
  4313. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error initalizing playback device from specified object hr=0x%x", hr );
  4314. delete tmpDevice;
  4315. return hr;
  4316. }
  4317. m_audioPlaybackDevice = tmpDevice;
  4318. }
  4319. // If they gave us an object, just use it
  4320. if( m_dvSoundDeviceConfig.lpdsCaptureDevice != NULL )
  4321. {
  4322. CDirectSoundCaptureRecordDevice *tmpRecDevice;
  4323. tmpRecDevice = new CDirectSoundCaptureRecordDevice( );
  4324. if( tmpRecDevice == NULL )
  4325. {
  4326. if( m_audioPlaybackDevice != NULL )
  4327. {
  4328. delete m_audioPlaybackDevice;
  4329. m_audioPlaybackDevice = NULL;
  4330. }
  4331. Diagnostics_Write( DVF_ERRORLEVEL, "Failed to alloc memory" );
  4332. return DVERR_OUTOFMEMORY;
  4333. }
  4334. hr = tmpRecDevice->Initialize( m_dvSoundDeviceConfig.lpdsCaptureDevice, m_dvSoundDeviceConfig.guidCaptureDevice );
  4335. if( FAILED( hr ) )
  4336. {
  4337. Diagnostics_Write( DVF_ERRORLEVEL, "Error initializing record device from specified object hr=0x%x", hr );
  4338. if( m_audioPlaybackDevice != NULL )
  4339. {
  4340. delete m_audioPlaybackDevice;
  4341. m_audioPlaybackDevice = NULL;
  4342. }
  4343. delete tmpRecDevice;
  4344. return hr;
  4345. }
  4346. m_audioRecordDevice = tmpRecDevice;
  4347. }
  4348. // We were passed a buffer by the user
  4349. if( m_dvSoundDeviceConfig.lpdsMainBuffer )
  4350. {
  4351. m_audioPlaybackBuffer = new CDirectSoundPlaybackBuffer( m_dvSoundDeviceConfig.lpdsMainBuffer );
  4352. if( !m_audioPlaybackBuffer )
  4353. {
  4354. Diagnostics_Write( DVF_ERRORLEVEL, "Error allocating memory" );
  4355. if( m_audioPlaybackDevice )
  4356. {
  4357. delete m_audioPlaybackDevice;
  4358. m_audioPlaybackDevice = NULL;
  4359. }
  4360. if( m_audioRecordDevice )
  4361. {
  4362. delete m_audioRecordDevice;
  4363. m_audioRecordDevice = NULL;
  4364. }
  4365. return DVERR_OUTOFMEMORY;
  4366. }
  4367. DPFX(DPFPREP, DVF_INFOLEVEL, "Creating a buffer using buffer user gave us." );
  4368. }
  4369. // If we haven't initialized half duplex
  4370. if( !(m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_HALFDUPLEX) )
  4371. {
  4372. hr = InitFullDuplex( m_dvSoundDeviceConfig.hwndAppWindow,
  4373. m_dvSoundDeviceConfig.guidPlaybackDevice,
  4374. &m_audioPlaybackDevice,
  4375. &m_dsBufferDesc,
  4376. &m_audioPlaybackBuffer,
  4377. m_dvSoundDeviceConfig.guidCaptureDevice,
  4378. &m_audioRecordDevice,
  4379. &m_audioRecordBuffer,
  4380. m_lpdvfCompressionInfo->guidType,
  4381. this->s_lpwfxPrimaryFormat,
  4382. this->s_lpwfxPlaybackFormat,
  4383. this->s_fASO,
  4384. this->m_dvSoundDeviceConfig.dwMainBufferPriority,
  4385. this->m_dvSoundDeviceConfig.dwMainBufferFlags,
  4386. m_dvSoundDeviceConfig.dwFlags );
  4387. // Full duplex init failed, set the half duplex flag
  4388. if( FAILED( hr ) )
  4389. {
  4390. // Records are deleted here because not needed for half-duplex
  4391. if( m_audioRecordDevice != NULL )
  4392. {
  4393. delete m_audioRecordDevice;
  4394. m_audioRecordDevice = NULL;
  4395. }
  4396. if( m_audioRecordBuffer != NULL )
  4397. {
  4398. delete m_audioRecordBuffer;
  4399. m_audioRecordBuffer = NULL;
  4400. }
  4401. // If we got playbacks from user then let them fall through to half-duplex
  4402. // If we allocated them in InitFullDuplex - then it cleans up for itself on error
  4403. m_dvSoundDeviceConfig.dwFlags |= DVSOUNDCONFIG_HALFDUPLEX;
  4404. }
  4405. // Full duplex passed
  4406. else
  4407. {
  4408. }
  4409. if( hr == E_OUTOFMEMORY )
  4410. {
  4411. DPFX(DPFPREP, DVF_ERRORLEVEL, "Full duplex init received an E_OUTOFMEMORY, failing Initialization()." );
  4412. return DVERR_OUTOFMEMORY;
  4413. }
  4414. }
  4415. if( m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_HALFDUPLEX )
  4416. {
  4417. hr = InitHalfDuplex( m_dvSoundDeviceConfig.hwndAppWindow,
  4418. m_dvSoundDeviceConfig.guidPlaybackDevice,
  4419. &m_audioPlaybackDevice,
  4420. &m_dsBufferDesc,
  4421. &m_audioPlaybackBuffer,
  4422. m_lpdvfCompressionInfo->guidType,
  4423. this->s_lpwfxPrimaryFormat,
  4424. this->s_lpwfxPlaybackFormat,
  4425. this->m_dvSoundDeviceConfig.dwMainBufferPriority,
  4426. this->m_dvSoundDeviceConfig.dwMainBufferFlags,
  4427. m_dvSoundDeviceConfig.dwFlags );
  4428. if( FAILED( hr ) )
  4429. {
  4430. // if user supplied these then we need to nuke them here
  4431. // if user didn't supply, then they were nuked on error in InitHalfDuplex
  4432. if( m_audioPlaybackDevice != NULL )
  4433. {
  4434. delete m_audioPlaybackDevice;
  4435. m_audioPlaybackDevice = NULL;
  4436. }
  4437. if( m_audioPlaybackBuffer != NULL )
  4438. {
  4439. delete m_audioPlaybackBuffer;
  4440. m_audioPlaybackBuffer = NULL;
  4441. }
  4442. return hr;
  4443. }
  4444. }
  4445. // Build the frame pool
  4446. DNEnterCriticalSection( &CDirectVoiceEngine::s_csSTLLock );
  4447. m_pFramePool = new CFramePool( m_dwCompressedFrameSize );
  4448. DNLeaveCriticalSection( &CDirectVoiceEngine::s_csSTLLock );
  4449. m_lpstBufferList = NULL;
  4450. if( m_pFramePool == NULL )
  4451. {
  4452. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to allocate frame pool" );
  4453. return DVERR_OUTOFMEMORY;
  4454. }
  4455. if (!m_pFramePool->Init())
  4456. {
  4457. delete m_pFramePool;
  4458. return DVERR_OUTOFMEMORY;
  4459. }
  4460. if( m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_SETCONVERSIONQUALITY )
  4461. {
  4462. if( m_dvSoundDeviceConfig.guidPlaybackDevice != GUID_NULL )
  4463. {
  4464. hr = m_audioPlaybackDevice->GetMixerQuality( &m_dwOriginalPlayQuality );
  4465. if( FAILED( hr ) )
  4466. {
  4467. DPFX(DPFPREP, 1, "Unable to get current playback quality hr=0x%x", hr );
  4468. m_dwOriginalPlayQuality = DV_CLIENT_SRCQUALITY_INVALID;
  4469. }
  4470. // We're already at the setting, someone else probably runinng, disable restore
  4471. else if( m_dwOriginalPlayQuality == DIRECTSOUNDMIXER_SRCQUALITY_ADVANCED )
  4472. {
  4473. DPFX(DPFPREP, 1, "Quality setting is already at correct value. Will not set/restore" );
  4474. m_dwOriginalPlayQuality = DV_CLIENT_SRCQUALITY_INVALID;
  4475. }
  4476. else
  4477. {
  4478. hr = m_audioPlaybackDevice->SetMixerQuality( DIRECTSOUNDMIXER_SRCQUALITY_ADVANCED );
  4479. }
  4480. }
  4481. else
  4482. {
  4483. hr = DVERR_NOTSUPPORTED;
  4484. }
  4485. if( FAILED( hr ) )
  4486. {
  4487. DPFX(DPFPREP, DVF_WARNINGLEVEL, "Unable to set mixer quality for playback device hr=0x%x", hr );
  4488. }
  4489. }
  4490. else
  4491. {
  4492. m_dwOriginalPlayQuality = DV_CLIENT_SRCQUALITY_INVALID;
  4493. }
  4494. // Add a reference for the soundconfig struct if one doesn't exist
  4495. if( m_dvSoundDeviceConfig.lpdsPlaybackDevice == NULL )
  4496. {
  4497. m_dvSoundDeviceConfig.lpdsPlaybackDevice = m_audioPlaybackDevice->GetPlaybackDevice();
  4498. m_dvSoundDeviceConfig.lpdsPlaybackDevice->AddRef();
  4499. }
  4500. // If we're not half duplex, do some initial sets for the recording system
  4501. if( !(m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_HALFDUPLEX) &&
  4502. m_audioRecordDevice != NULL )
  4503. {
  4504. if( m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_SETCONVERSIONQUALITY )
  4505. {
  4506. DPFX(DPFPREP, DVF_INFOLEVEL, "Setting quality" );
  4507. DV_DUMP_GUID( m_dvSoundDeviceConfig.guidCaptureDevice );
  4508. if( m_dvSoundDeviceConfig.lpdsCaptureDevice == NULL )
  4509. {
  4510. m_dvSoundDeviceConfig.lpdsCaptureDevice = m_audioRecordDevice->GetCaptureDevice();
  4511. m_dvSoundDeviceConfig.lpdsCaptureDevice->AddRef();
  4512. }
  4513. if( m_dvSoundDeviceConfig.guidCaptureDevice != GUID_NULL )
  4514. {
  4515. DPFX(DPFPREP, DVF_INFOLEVEL, "Setting quality 2" );
  4516. hr = m_audioRecordDevice->GetMixerQuality( &m_dwOriginalRecordQuality );
  4517. if( FAILED( hr ) )
  4518. {
  4519. DPFX(DPFPREP, DVF_WARNINGLEVEL, "Unable to get conversion quality hr=0x%x", hr );
  4520. m_dwOriginalRecordQuality = DV_CLIENT_SRCQUALITY_INVALID;
  4521. }
  4522. // We're already at the setting, someone else probably runinng, disable restore
  4523. else if( m_dwOriginalRecordQuality == DIRECTSOUNDMIXER_SRCQUALITY_ADVANCED )
  4524. {
  4525. DPFX(DPFPREP, 1, "Play Quality setting is already at correct value. Will not set/restore" );
  4526. m_dwOriginalRecordQuality = DV_CLIENT_SRCQUALITY_INVALID;
  4527. }
  4528. else
  4529. {
  4530. hr = m_audioRecordDevice->SetMixerQuality( DIRECTSOUNDMIXER_SRCQUALITY_ADVANCED );
  4531. }
  4532. }
  4533. else
  4534. {
  4535. hr = DVERR_NOTSUPPORTED;
  4536. }
  4537. if( FAILED( hr ) )
  4538. {
  4539. DPFX(DPFPREP, DVF_WARNINGLEVEL, "Setting failed" );
  4540. DPFX(DPFPREP, DVF_WARNINGLEVEL, "Unable to set mixer quality for record device hr=0x%x", hr );
  4541. }
  4542. }
  4543. else
  4544. {
  4545. m_dwOriginalRecordQuality = DV_CLIENT_SRCQUALITY_INVALID;
  4546. }
  4547. // If we haven't set no focus
  4548. if( !(m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_NOFOCUS ) )
  4549. {
  4550. // Recording is started muted
  4551. if( m_dvClientConfig.dwFlags & DVCLIENTCONFIG_RECORDMUTE )
  4552. {
  4553. DPFX(DPFPREP, DVF_INFOLEVEL, "Record Muted: Yielding focus" );
  4554. hr = m_audioRecordBuffer->YieldFocus();
  4555. }
  4556. else
  4557. {
  4558. DPFX(DPFPREP, DVF_INFOLEVEL, "Record Un-Muted: Attempting to reclaim focus" );
  4559. hr = m_audioRecordBuffer->ClaimFocus();
  4560. }
  4561. if( FAILED( hr ) )
  4562. {
  4563. DPFX(DPFPREP, DVF_WARNINGLEVEL, "Focus set failed hr=0x%x", hr );
  4564. }
  4565. }
  4566. LONG lVolume;
  4567. hr = m_audioRecordBuffer->GetVolume( &lVolume );
  4568. if( FAILED( hr ) )
  4569. {
  4570. Diagnostics_Write( 0, "Unable to retrieve recording volume hr=0x%x", hr );
  4571. Diagnostics_Write(0, "Disabling recording controls" );
  4572. m_dvSoundDeviceConfig.dwFlags |= DVSOUNDCONFIG_NORECVOLAVAILABLE;
  4573. }
  4574. }
  4575. else
  4576. {
  4577. m_dvSoundDeviceConfig.lpdsCaptureDevice = NULL;
  4578. }
  4579. // Initialize the sound target list
  4580. InitSoundTargetList();
  4581. hr = CreateGeneralBuffer();
  4582. if( FAILED( hr ) )
  4583. {
  4584. Diagnostics_Write( 0, "Error creating general buffer hr=0x%x", hr );
  4585. return hr;
  4586. }
  4587. DPFX(DPFPREP, DVF_ENTRYLEVEL, "DVCE::InitializeSoundSystem() End" );
  4588. return DV_OK;
  4589. }
  4590. #undef DPF_MODNAME
  4591. #define DPF_MODNAME "CDirectVoiceClientEngine::ShutdownSoundSystem"
  4592. // ShutdownSoundSystem
  4593. //
  4594. // Stop the sound system
  4595. //
  4596. HRESULT CDirectVoiceClientEngine::ShutdownSoundSystem()
  4597. {
  4598. DPFX(DPFPREP, DVF_ENTRYLEVEL, "DVCE::ShutdownSoundSystem() Begin" );
  4599. HRESULT hr;
  4600. FreeSoundTargetList();
  4601. #ifndef __DISABLE_SOUND
  4602. if( m_audioRecordBuffer != NULL )
  4603. {
  4604. delete m_audioRecordBuffer;
  4605. m_audioRecordBuffer = NULL;
  4606. }
  4607. if( m_dvSoundDeviceConfig.lpdsMainBuffer != NULL )
  4608. {
  4609. m_dvSoundDeviceConfig.lpdsMainBuffer->Release();
  4610. m_dvSoundDeviceConfig.lpdsMainBuffer = NULL;
  4611. }
  4612. if( m_audioRecordDevice != NULL )
  4613. {
  4614. if( m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_SETCONVERSIONQUALITY )
  4615. {
  4616. if( m_dwOriginalRecordQuality != DV_CLIENT_SRCQUALITY_INVALID )
  4617. {
  4618. hr = m_audioRecordDevice->SetMixerQuality( m_dwOriginalRecordQuality );
  4619. if( FAILED( hr ) )
  4620. {
  4621. DPFX(DPFPREP, 1, "Failed to restore original recording quality hr=0x%x", hr );
  4622. }
  4623. }
  4624. m_dwOriginalRecordQuality = DV_CLIENT_SRCQUALITY_INVALID;
  4625. }
  4626. delete m_audioRecordDevice;
  4627. m_audioRecordDevice = NULL;
  4628. }
  4629. if( m_audioPlaybackBuffer != NULL )
  4630. {
  4631. delete m_audioPlaybackBuffer;
  4632. m_audioPlaybackBuffer = NULL;
  4633. }
  4634. if( m_audioPlaybackDevice != NULL )
  4635. {
  4636. if( m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_SETCONVERSIONQUALITY )
  4637. {
  4638. if( m_dwOriginalPlayQuality != DV_CLIENT_SRCQUALITY_INVALID )
  4639. {
  4640. hr = m_audioPlaybackDevice->SetMixerQuality( m_dwOriginalPlayQuality );
  4641. if( FAILED( hr ) )
  4642. {
  4643. DPFX(DPFPREP, 1, "Failed to restore original playback quality hr=0x%x", hr );
  4644. }
  4645. }
  4646. m_dwOriginalPlayQuality = DV_CLIENT_SRCQUALITY_INVALID;
  4647. }
  4648. delete m_audioPlaybackDevice;
  4649. m_audioPlaybackDevice = NULL;
  4650. }
  4651. #endif
  4652. if( m_dvSoundDeviceConfig.lpdsCaptureDevice != NULL )
  4653. {
  4654. m_dvSoundDeviceConfig.lpdsCaptureDevice->Release();
  4655. m_dvSoundDeviceConfig.lpdsCaptureDevice = NULL;
  4656. }
  4657. if( m_dvSoundDeviceConfig.lpdsPlaybackDevice != NULL )
  4658. {
  4659. m_dvSoundDeviceConfig.lpdsPlaybackDevice->Release();
  4660. m_dvSoundDeviceConfig.lpdsPlaybackDevice = NULL;
  4661. }
  4662. DPFX(DPFPREP, DVF_ENTRYLEVEL, "DVCE::ShutdownSoundSystem() End" );
  4663. Diagnostics_End();
  4664. return DV_OK;
  4665. }
  4666. #undef DPF_MODNAME
  4667. #define DPF_MODNAME "CDirectVoiceClientEngine::SetConnectResult"
  4668. // SetConnectResult
  4669. //
  4670. // This function stores the specified connect result code in m_hrOriginalConnectResult
  4671. // and stores the "translated" error code in m_hrConnectResult. m_hrConnectResult
  4672. // is used to return the result to the user.
  4673. //
  4674. // For some error codes the voice layer translates the code to a voice specific error,
  4675. // this is why this function is required.
  4676. //
  4677. void CDirectVoiceClientEngine::SetConnectResult( HRESULT hrConnectResult )
  4678. {
  4679. DPFX( DPFPREP, DVF_CONNECT_PROCEDURE_DEBUG_LEVEL, "CONNECT RESULT: Transition [0x%x] to [0x%x]", m_hrOriginalConnectResult, hrConnectResult );
  4680. m_hrOriginalConnectResult = hrConnectResult;
  4681. if( HRESULT_FACILITY( hrConnectResult ) == static_cast<HRESULT>(_FACDS) )
  4682. {
  4683. if( hrConnectResult == DSERR_ALLOCATED )
  4684. {
  4685. DPFX( DPFPREP, DVF_WARNINGLEVEL, "WARNING: Mapping DSERR_ALLOCATED --> DVERR_PLAYBACKSYSTEMERROR" );
  4686. m_hrConnectResult = DVERR_PLAYBACKSYSTEMERROR;
  4687. }
  4688. else if( hrConnectResult == DSERR_OUTOFMEMORY )
  4689. {
  4690. DPFX( DPFPREP, DVF_WARNINGLEVEL, "WARNING: Mapping DSERR_OUTOFMEMORY --> DVERR_OUTOFMEMORY" );
  4691. m_hrConnectResult = DVERR_OUTOFMEMORY;
  4692. }
  4693. else if( hrConnectResult == DSERR_NODRIVER )
  4694. {
  4695. DPFX( DPFPREP, DVF_WARNINGLEVEL, "WARNING: Mapping DSERR_NODRIVER --> DVERR_INVALIDDEVICE" );
  4696. m_hrConnectResult = DVERR_INVALIDDEVICE;
  4697. }
  4698. else
  4699. {
  4700. DPFX( DPFPREP, DVF_WARNINGLEVEL, "WARNING: Mapping 0x%x --> DVERR_SOUNDINITFAILURE", hrConnectResult );
  4701. m_hrConnectResult = DVERR_SOUNDINITFAILURE;
  4702. }
  4703. }
  4704. else
  4705. {
  4706. m_hrConnectResult = hrConnectResult;
  4707. }
  4708. }
  4709. #undef DPF_MODNAME
  4710. #define DPF_MODNAME "CDirectVoiceClientEngine::GetConnectResult"
  4711. // GetConnectResult
  4712. //
  4713. // This function stores the specified connect result code in m_hrOriginalConnectResult
  4714. // and stores the "translated" error code in m_hrConnectResult. m_hrConnectResult
  4715. // is used to return the result to the user.
  4716. //
  4717. // For some error codes the voice layer translates the code to a voice specific error,
  4718. // this is why this function is required.
  4719. //
  4720. HRESULT CDirectVoiceClientEngine::GetConnectResult( ) const
  4721. {
  4722. return m_hrConnectResult;
  4723. }
  4724. #undef DPF_MODNAME
  4725. #define DPF_MODNAME "CDirectVoiceClientEngine::SendConnectResult"
  4726. HRESULT CDirectVoiceClientEngine::SendConnectResult()
  4727. {
  4728. if( m_fConnectAsync )
  4729. {
  4730. DVMSG_CONNECTRESULT dvConnect;
  4731. dvConnect.hrResult = GetConnectResult();
  4732. dvConnect.dwSize = sizeof( DVMSG_CONNECTRESULT );
  4733. return NotifyQueue_Add( DVMSGID_CONNECTRESULT, &dvConnect, sizeof( DVMSG_CONNECTRESULT ) );
  4734. }
  4735. else
  4736. {
  4737. return DV_OK;
  4738. }
  4739. }
  4740. #undef DPF_MODNAME
  4741. #define DPF_MODNAME "CDirectVoiceClientEngine::SendDisconnectResult"
  4742. HRESULT CDirectVoiceClientEngine::SendDisconnectResult()
  4743. {
  4744. if( m_fDisconnectAsync )
  4745. {
  4746. DVMSG_DISCONNECTRESULT dvDisconnect;
  4747. dvDisconnect.hrResult = m_hrDisconnectResult;
  4748. dvDisconnect.dwSize = sizeof( DVMSG_DISCONNECTRESULT );
  4749. return NotifyQueue_Add( DVMSGID_DISCONNECTRESULT, &dvDisconnect, sizeof( DVMSG_DISCONNECTRESULT ) );
  4750. }
  4751. else
  4752. {
  4753. return DV_OK;
  4754. }
  4755. }
  4756. // NotifyThread
  4757. //
  4758. // All-purpose watch/notification thread.
  4759. //
  4760. // Wakes up to:
  4761. // - Check for multicast player timeout
  4762. // - Adjust parameters on notification
  4763. // - Sends notification messages to users.
  4764. // - Checks for timeouts on connect and disconnect calls
  4765. // - Sends level notifications
  4766. //
  4767. #undef DPF_MODNAME
  4768. #define DPF_MODNAME "CDirectVoiceClientEngine::NotifyThread"
  4769. void CDirectVoiceClientEngine::NotifyThread( void *lpParam )
  4770. {
  4771. CDirectVoiceClientEngine *This = (CDirectVoiceClientEngine *) lpParam;
  4772. HANDLE eventArray[5];
  4773. DWORD dwWaitPeriod;
  4774. LONG lWaitResult;
  4775. DWORD dwPowerLevel;
  4776. DWORD dwLastLevelNotify,
  4777. dwLastTimeoutCheck,
  4778. dwCurTime;
  4779. DVMSG_INPUTLEVEL dvInputLevel;
  4780. DVMSG_OUTPUTLEVEL dvOutputLevel;
  4781. dvInputLevel.dwSize = sizeof( DVMSG_INPUTLEVEL );
  4782. dvOutputLevel.dwSize = sizeof( DVMSG_OUTPUTLEVEL );
  4783. HRESULT hr;
  4784. DVID dvidMessageTarget;
  4785. hr = COM_CoInitialize(NULL);
  4786. if( FAILED( hr ) )
  4787. {
  4788. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error initializing COM" );
  4789. SetEvent( This->m_hNotifyDone );
  4790. This->HandleThreadError( DVERR_GENERIC );
  4791. return;
  4792. }
  4793. eventArray[0] = This->m_hNotifyTerminate;
  4794. eventArray[1] = This->m_hNotifyChange;
  4795. eventArray[2] = This->m_hNotifyDisconnect;
  4796. eventArray[3] = This->m_hNewNotifyElement;
  4797. eventArray[4] = This->m_hNotifyConnect;
  4798. // Setup last times we checked to right now.
  4799. dwLastLevelNotify = GetTickCount();
  4800. dwLastTimeoutCheck = dwLastLevelNotify;
  4801. dwWaitPeriod = DV_CLIENT_NOTIFYWAKEUP_TIMEOUT;
  4802. while( 1 )
  4803. {
  4804. lWaitResult = WaitForMultipleObjects( 5, eventArray, FALSE, dwWaitPeriod );
  4805. DPFX(DPFPREP, DVF_INFOLEVEL, "Wakeing up!" );
  4806. // If we were woken up for a reason, handle the reason first
  4807. if( lWaitResult != WAIT_TIMEOUT )
  4808. {
  4809. lWaitResult -= WAIT_OBJECT_0;
  4810. if( lWaitResult == 0 )
  4811. {
  4812. DPFX(DPFPREP, DVF_INFOLEVEL, "Shutting down" );
  4813. break;
  4814. }
  4815. else if( lWaitResult == 1 )
  4816. {
  4817. DPFX(DPFPREP, DVF_INFOLEVEL, "Parameter Change" );
  4818. }
  4819. else if( lWaitResult == 2 )
  4820. {
  4821. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Processing disconnect request" );
  4822. This->DoDisconnect();
  4823. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Finished processing disconnect" );
  4824. continue;
  4825. }
  4826. else if( lWaitResult == 3 )
  4827. {
  4828. hr = This->NotifyQueue_IndicateNext();
  4829. if( FAILED( hr ) )
  4830. {
  4831. DPFX(DPFPREP, DVF_ERRORLEVEL, "NotifyQueue_Get Failed hr=0x%x", hr );
  4832. }
  4833. }
  4834. else if( lWaitResult == 4 )
  4835. {
  4836. if( FAILED( This->GetConnectResult() ) )
  4837. {
  4838. This->Cleanup();
  4839. This->SendConnectResult();
  4840. SetEvent( This->m_hConnectAck );
  4841. }
  4842. else
  4843. {
  4844. This->DoConnectResponse();
  4845. }
  4846. continue;
  4847. }
  4848. }
  4849. if( This->m_dwCurrentState == DVCSTATE_IDLE )
  4850. {
  4851. continue;
  4852. }
  4853. // If we're connecting, check for timeout on the connection
  4854. // request.
  4855. if( This->m_dwCurrentState == DVCSTATE_CONNECTING && This->m_dwSynchBegin != 0 )
  4856. {
  4857. dwCurTime = GetTickCount();
  4858. if( ( dwCurTime - This->m_dwSynchBegin ) > DV_CLIENT_CONNECT_TIMEOUT )
  4859. {
  4860. DPFX(DPFPREP, DVF_ERRORLEVEL, "Connection Timed-Out. Returning NOVOICESESSION" );
  4861. This->SetConnectResult( DVERR_NOVOICESESSION );
  4862. This->Cleanup();
  4863. This->SendConnectResult();
  4864. SetEvent( This->m_hConnectAck );
  4865. continue;
  4866. }
  4867. else if( ( dwCurTime - This->m_dwLastConnectSent ) > DV_CLIENT_CONNECT_RETRY_TIMEOUT )
  4868. {
  4869. DPFX(DPFPREP, DVF_WARNINGLEVEL, "Connect Request Timed-Out" );
  4870. DPFX(DPFPREP, DVF_WARNINGLEVEL, "Re-sending connection request" );
  4871. hr = This->Send_ConnectRequest();
  4872. if( FAILED( hr ) )
  4873. {
  4874. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error sending connection request. Send error hr=0x%x", hr );
  4875. This->SetConnectResult( DVERR_SENDERROR );
  4876. This->Cleanup();
  4877. This->SendConnectResult();
  4878. SetEvent( This->m_hConnectAck );
  4879. continue;
  4880. }
  4881. This->m_dwLastConnectSent = dwCurTime;
  4882. }
  4883. }
  4884. // If we're disconnecting, check for timeout on the disconnect
  4885. else if( This->m_dwCurrentState == DVCSTATE_DISCONNECTING )
  4886. {
  4887. dwCurTime = GetTickCount();
  4888. DPFX(DPFPREP, DVF_INFOLEVEL, "Checking timeout on disconnect. Waited %d so far", dwCurTime - This->m_dwSynchBegin );
  4889. if( ( dwCurTime - This->m_dwSynchBegin ) > DV_CLIENT_DISCONNECT_TIMEOUT )
  4890. {
  4891. DPFX(DPFPREP, DVF_WARNINGLEVEL, "Disconnect Request Timed-Out" );
  4892. This->DoSignalDisconnect( DVERR_TIMEOUT );
  4893. continue;
  4894. }
  4895. }
  4896. // Take care of the periodic checks
  4897. if (This->m_dwCurrentState == DVCSTATE_CONNECTED)
  4898. {
  4899. dwCurTime = GetTickCount();
  4900. // Update pending / other lists
  4901. This->UpdateActiveNotifyPendingList( );
  4902. // If we're running a multicast session.. check for timed-out users
  4903. if( This->m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_FORWARDING )
  4904. {
  4905. if( ( dwCurTime - dwLastTimeoutCheck ) > DV_MULTICAST_USERTIMEOUT_PERIOD )
  4906. {
  4907. This->CheckForUserTimeout(dwCurTime);
  4908. dwLastTimeoutCheck = dwCurTime;
  4909. }
  4910. }
  4911. // Check to see if it's time to notify about levels
  4912. if( This->m_dvClientConfig.dwNotifyPeriod != 0 && This->m_fLocalPlayerAvailable )
  4913. {
  4914. if( ( dwCurTime - dwLastLevelNotify ) > This->m_dvClientConfig.dwNotifyPeriod )
  4915. {
  4916. dvInputLevel.pvLocalPlayerContext = This->m_pvLocalPlayerContext;
  4917. dvOutputLevel.pvLocalPlayerContext = This->m_pvLocalPlayerContext;
  4918. if( !(This->m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_HALFDUPLEX) )
  4919. {
  4920. dvInputLevel.dwPeakLevel = This->m_bLastPeak;
  4921. if( This->m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_NORECVOLAVAILABLE )
  4922. {
  4923. dvInputLevel.lRecordVolume = DSBVOLUME_MAX;
  4924. }
  4925. else
  4926. {
  4927. dvInputLevel.lRecordVolume = This->m_dvClientConfig.lRecordVolume;
  4928. }
  4929. This->NotifyQueue_Add( DVMSGID_INPUTLEVEL, &dvInputLevel, sizeof( DVMSG_INPUTLEVEL ) );
  4930. }
  4931. dvOutputLevel.dwPeakLevel = This->m_bLastPlaybackPeak;
  4932. dvOutputLevel.lOutputVolume = This->m_dvClientConfig.lPlaybackVolume;
  4933. This->NotifyQueue_Add( DVMSGID_OUTPUTLEVEL, &dvOutputLevel, sizeof( DVMSG_OUTPUTLEVEL ) );
  4934. This->SendPlayerLevels();
  4935. dwLastLevelNotify = dwCurTime;
  4936. }
  4937. }
  4938. }
  4939. }
  4940. // Flush out any remaining notifications
  4941. This->NotifyQueue_Flush();
  4942. SetEvent( This->m_hNotifyDone );
  4943. COM_CoUninitialize();
  4944. _endthread();
  4945. }
  4946. #undef DPF_MODNAME
  4947. #define DPF_MODNAME "CDirectVoiceClientEngine::SendPlayerLevels"
  4948. //
  4949. // SendPlayerLevels
  4950. //
  4951. // Send player level notifications, but only if there is a message handler
  4952. // and the player level message is active.
  4953. //
  4954. void CDirectVoiceClientEngine::SendPlayerLevels()
  4955. {
  4956. if( CheckShouldSendMessage( DVMSGID_PLAYEROUTPUTLEVEL ) )
  4957. {
  4958. DPFX(DPFPREP, DVF_INFOLEVEL, "SendPlayerLevels: Got Lock" );
  4959. CVoicePlayer *pCurrentPlayer;
  4960. CBilink *pblSearch;
  4961. DVMSG_PLAYEROUTPUTLEVEL dvPlayerLevel;
  4962. pblSearch = m_blNotifyActivePlayers.GetNext();
  4963. while( pblSearch != &m_blNotifyActivePlayers )
  4964. {
  4965. pCurrentPlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blNotifyList );
  4966. ASSERT_VPLAYER(pCurrentPlayer);
  4967. if( pCurrentPlayer == NULL )
  4968. {
  4969. DPFX(DPFPREP, DVF_ERRORLEVEL, "Retrieved NULL player from active list" );
  4970. DNASSERT( FALSE );
  4971. return;
  4972. }
  4973. if( pCurrentPlayer->IsReceiving() )
  4974. {
  4975. dvPlayerLevel.dwSize = sizeof( DVMSG_PLAYEROUTPUTLEVEL );
  4976. dvPlayerLevel.dvidSourcePlayerID = pCurrentPlayer->GetPlayerID();
  4977. dvPlayerLevel.dwPeakLevel = pCurrentPlayer->GetLastPeak();
  4978. dvPlayerLevel.pvPlayerContext = pCurrentPlayer->GetContext();
  4979. NotifyQueue_Add( DVMSGID_PLAYEROUTPUTLEVEL, &dvPlayerLevel, sizeof( DVMSG_PLAYEROUTPUTLEVEL ) );
  4980. }
  4981. pblSearch = pblSearch->GetNext();
  4982. }
  4983. }
  4984. DPFX(DPFPREP, DVF_INFOLEVEL, "SendPlayerLevels: Done Enum" );
  4985. return;
  4986. }
  4987. #undef DPF_MODNAME
  4988. #define DPF_MODNAME "CDirectVoiceClientEngine::CheckForUserTimeout"
  4989. //
  4990. // CheckForUserTimeout
  4991. //
  4992. // Run the list of users and check for user timeouts in multicast sessions
  4993. //
  4994. void CDirectVoiceClientEngine::CheckForUserTimeout( DWORD dwCurTime )
  4995. {
  4996. DPFX(DPFPREP, DVF_INFOLEVEL, "Got Lock" );
  4997. CBilink *pblSearch;
  4998. CVoicePlayer *pCurrentPlayer;
  4999. DVPROTOCOLMSG_PLAYERQUIT msgPlayerQuit;
  5000. pblSearch = m_blNotifyActivePlayers.GetNext();
  5001. msgPlayerQuit.dwType = DVMSGID_DELETEVOICEPLAYER;
  5002. while( pblSearch != &m_blNotifyActivePlayers )
  5003. {
  5004. pCurrentPlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blNotifyList );
  5005. ASSERT_VPLAYER(pCurrentPlayer);
  5006. if( dwCurTime - pCurrentPlayer->GetLastPlayback() > DV_MULTICAST_USERTIMEOUT_PERIOD )
  5007. {
  5008. msgPlayerQuit.dvidID = pCurrentPlayer->GetPlayerID();
  5009. HandleDeleteVoicePlayer( pCurrentPlayer->GetPlayerID(), &msgPlayerQuit, sizeof( DVPROTOCOLMSG_PLAYERQUIT ) );
  5010. }
  5011. pblSearch = pblSearch->GetNext();
  5012. }
  5013. DPFX(DPFPREP, DVF_INFOLEVEL, "Done Enum" );
  5014. return;
  5015. }
  5016. // Cleanup any outstanding entries on the playback lists
  5017. void CDirectVoiceClientEngine::CleanupPlaybackLists( )
  5018. {
  5019. CBilink *pblSearch;
  5020. CVoicePlayer *pVoicePlayer;
  5021. DNEnterCriticalSection( &m_csPlayAddList );
  5022. m_dwPlayActiveCount = 0;
  5023. pblSearch = m_blPlayActivePlayers.GetNext();
  5024. while( pblSearch != &m_blPlayActivePlayers )
  5025. {
  5026. pVoicePlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blPlayList );
  5027. ASSERT_VPLAYER(pVoicePlayer);
  5028. pVoicePlayer->RemoveFromPlayList();
  5029. pVoicePlayer->Release();
  5030. pblSearch = m_blPlayActivePlayers.GetNext();
  5031. }
  5032. pblSearch = m_blPlayAddPlayers.GetNext();
  5033. while( pblSearch != &m_blPlayAddPlayers )
  5034. {
  5035. pVoicePlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blPlayList );
  5036. ASSERT_VPLAYER(pVoicePlayer);
  5037. pVoicePlayer->RemoveFromPlayList();
  5038. pVoicePlayer->Release();
  5039. pblSearch = m_blPlayAddPlayers.GetNext();
  5040. }
  5041. DNLeaveCriticalSection( &m_csPlayAddList );
  5042. }
  5043. // Cleanup any outstanding entries on the playback lists
  5044. void CDirectVoiceClientEngine::CleanupNotifyLists( )
  5045. {
  5046. CBilink *pblSearch;
  5047. CVoicePlayer *pVoicePlayer;
  5048. DNEnterCriticalSection( &m_csNotifyAddList );
  5049. pblSearch = m_blNotifyActivePlayers.GetNext();
  5050. while( pblSearch != &m_blNotifyActivePlayers )
  5051. {
  5052. pVoicePlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blNotifyList );
  5053. ASSERT_VPLAYER(pVoicePlayer);
  5054. pVoicePlayer->RemoveFromNotifyList();
  5055. pVoicePlayer->Release();
  5056. pblSearch = m_blNotifyActivePlayers.GetNext();
  5057. }
  5058. pblSearch = m_blNotifyAddPlayers.GetNext();
  5059. while( pblSearch != &m_blNotifyAddPlayers )
  5060. {
  5061. pVoicePlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blNotifyList );
  5062. ASSERT_VPLAYER(pVoicePlayer);
  5063. pVoicePlayer->RemoveFromNotifyList();
  5064. pVoicePlayer->Release();
  5065. pblSearch = m_blNotifyAddPlayers.GetNext();
  5066. }
  5067. DNLeaveCriticalSection( &m_csNotifyAddList );
  5068. }
  5069. // UpdateActivePendingList
  5070. //
  5071. // This function looks at the pending list and moves those elements on the pending list to the active list
  5072. //
  5073. // This function also looks at the active list and removes those players who are disconnected
  5074. //
  5075. // There are three four lists in the system:
  5076. // - Playback Thread
  5077. // - Notify Thread
  5078. // - Host Migration List
  5079. //
  5080. void CDirectVoiceClientEngine::UpdateActivePlayPendingList( )
  5081. {
  5082. CBilink *pblSearch;
  5083. CVoicePlayer *pVoicePlayer;
  5084. DNEnterCriticalSection( &m_csPlayAddList );
  5085. // Add players who are pending
  5086. pblSearch = m_blPlayAddPlayers.GetNext();
  5087. while( pblSearch != &m_blPlayAddPlayers )
  5088. {
  5089. pVoicePlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blPlayList );
  5090. ASSERT_VPLAYER(pVoicePlayer);
  5091. pVoicePlayer->RemoveFromPlayList();
  5092. pVoicePlayer->AddToPlayList( &m_blPlayActivePlayers );
  5093. m_dwPlayActiveCount++;
  5094. pblSearch = m_blPlayAddPlayers.GetNext();
  5095. }
  5096. DNLeaveCriticalSection( &m_csPlayAddList );
  5097. // Remove players who have disconnected
  5098. pblSearch = m_blPlayActivePlayers.GetNext();
  5099. while( pblSearch != &m_blPlayActivePlayers )
  5100. {
  5101. pVoicePlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blPlayList );
  5102. ASSERT_VPLAYER(pVoicePlayer);
  5103. pblSearch = pblSearch->GetNext();
  5104. // If current player has disconnected, remove them from active list
  5105. // and release the reference the list has
  5106. if( pVoicePlayer->IsDisconnected() )
  5107. {
  5108. m_dwPlayActiveCount--;
  5109. pVoicePlayer->RemoveFromPlayList();
  5110. pVoicePlayer->Release();
  5111. }
  5112. }
  5113. }
  5114. void CDirectVoiceClientEngine::UpdateActiveNotifyPendingList( )
  5115. {
  5116. CBilink *pblSearch;
  5117. CVoicePlayer *pVoicePlayer;
  5118. DNEnterCriticalSection( &m_csNotifyAddList );
  5119. // Add players who are pending
  5120. pblSearch = m_blNotifyAddPlayers.GetNext();
  5121. while( pblSearch != &m_blNotifyAddPlayers )
  5122. {
  5123. pVoicePlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blNotifyList );
  5124. ASSERT_VPLAYER(pVoicePlayer);
  5125. pVoicePlayer->RemoveFromNotifyList();
  5126. pVoicePlayer->AddToNotifyList( &m_blNotifyActivePlayers );
  5127. pblSearch = m_blNotifyAddPlayers.GetNext();
  5128. }
  5129. DNLeaveCriticalSection( &m_csNotifyAddList );
  5130. // Remove players who have disconnected
  5131. pblSearch = m_blNotifyActivePlayers.GetNext();
  5132. while( pblSearch != &m_blNotifyActivePlayers )
  5133. {
  5134. pVoicePlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blNotifyList );
  5135. ASSERT_VPLAYER(pVoicePlayer);
  5136. pblSearch = pblSearch->GetNext();
  5137. // If current player has disconnected, remove them from active list
  5138. // and release the reference the list has
  5139. if( pVoicePlayer->IsDisconnected() )
  5140. {
  5141. pVoicePlayer->RemoveFromNotifyList();
  5142. pVoicePlayer->Release();
  5143. }
  5144. }
  5145. }
  5146. HRESULT CDirectVoiceClientEngine::CreateGeneralBuffer( )
  5147. {
  5148. HRESULT hr;
  5149. m_lpstGeneralBuffer = new CSoundTarget( DVID_REMAINING,
  5150. m_audioPlaybackDevice,
  5151. (CAudioPlaybackBuffer *) m_audioPlaybackBuffer,
  5152. &m_dsBufferDesc,
  5153. m_dvSoundDeviceConfig.dwMainBufferPriority,
  5154. m_dvSoundDeviceConfig.dwMainBufferFlags,
  5155. m_dwUnCompressedFrameSize );
  5156. if( m_lpstGeneralBuffer == NULL )
  5157. {
  5158. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to allocate General Buffer" );
  5159. return DVERR_OUTOFMEMORY;
  5160. }
  5161. hr = m_lpstGeneralBuffer->GetInitResult();
  5162. if( FAILED( hr ) )
  5163. {
  5164. DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to initalize general buffer hr=0x%x", hr );
  5165. return hr;
  5166. }
  5167. if( m_lpstGeneralBuffer->Get3DBuffer() != NULL )
  5168. {
  5169. // Turn off 3D by default on the general buffer
  5170. m_lpstGeneralBuffer->Get3DBuffer()->SetMode( DS3DMODE_DISABLE, DS3D_IMMEDIATE );
  5171. }
  5172. // Set General Buffer Volume
  5173. if( m_lpstGeneralBuffer->GetBuffer() != NULL )
  5174. {
  5175. m_lpstGeneralBuffer->GetBuffer()->SetVolume( m_dvClientConfig.lPlaybackVolume );
  5176. }
  5177. hr = m_lpstGeneralBuffer->StartMix();
  5178. if( FAILED( hr ) )
  5179. {
  5180. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to start the mix hr=0x%x.", hr );
  5181. return hr;
  5182. }
  5183. return hr;
  5184. }
  5185. // New Playback Thread
  5186. #undef DPF_MODNAME
  5187. #define DPF_MODNAME "CDirectVoiceClientEngine::PlaybackThread"
  5188. void CDirectVoiceClientEngine::PlaybackThread( void *lParam )
  5189. {
  5190. DWORD dwCurPlayer;
  5191. BOOL bContinueEnum;
  5192. DVPROTOCOLMSG_PLAYERQUIT dvPlayerQuit;
  5193. DWORD dwResultSize;
  5194. LPBYTE lpSourceBuffer = NULL;
  5195. HANDLE hEventArray[2] = { NULL, NULL };
  5196. LONG lWaitResult;
  5197. BOOL fMixed;
  5198. CSoundTarget *lpstCurrent = NULL;
  5199. BYTE bHighPeak;
  5200. HRESULT hr;
  5201. DWORD dwEchoState;
  5202. DWORD dwTmp;
  5203. BYTE bTmpPeak1, bTmpPeak2;
  5204. DVMSG_PLAYERVOICESTART dvMsgPlayerVoiceStart;
  5205. DVMSG_PLAYERVOICESTOP dvMsgPlayerVoiceStop;
  5206. DWORD dwCompressStart;
  5207. DWORD dwCompressTime;
  5208. CVoicePlayer *pCurrentPlayer;
  5209. CBilink *pblSearch;
  5210. BOOL fSilence, fLost;
  5211. DWORD dwCurrentTime;
  5212. DWORD dwSeqNum, dwMsgNum;
  5213. CFrame *frTmpFrame;
  5214. DWORD dwCurrentLead = 0;
  5215. DWORD dwAllowedLeadBuffers = 0;
  5216. DWORD dwAllowedLeadBytes = 0;
  5217. DWORD dwHalfBufferSize = 0;
  5218. BOOL fDesktopCurrent = TRUE;
  5219. dvMsgPlayerVoiceStart.dwSize = sizeof( DVMSG_PLAYERVOICESTART );
  5220. dvMsgPlayerVoiceStop.dwSize = sizeof( DVMSG_PLAYERVOICESTOP );
  5221. CDirectVoiceClientEngine *This = (CDirectVoiceClientEngine *) lParam;
  5222. // Pre-calc some sizes..
  5223. dwAllowedLeadBuffers = DV_CLIENT_BASE_LEAD_MAX;
  5224. dwAllowedLeadBytes = This->m_dwUnCompressedFrameSize * DV_CLIENT_BASE_LEAD_MAX;
  5225. dwHalfBufferSize = This->m_dwUnCompressedFrameSize * (This->m_dwNumPerBuffer/2);
  5226. hr = COM_CoInitialize(NULL);
  5227. if( FAILED( hr ) )
  5228. {
  5229. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error initializing COM on playback thread" );
  5230. This->HandleThreadError( DVERR_GENERIC );
  5231. goto EXIT_PLAYBACK;
  5232. }
  5233. lpSourceBuffer = new BYTE[This->m_dwUnCompressedFrameSize];
  5234. if( lpSourceBuffer == NULL )
  5235. {
  5236. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to allocate source buffer" );
  5237. This->HandleThreadError( DVERR_OUTOFMEMORY );
  5238. goto EXIT_PLAYBACK;
  5239. }
  5240. hEventArray[0] = This->m_hPlaybackTerminate;
  5241. hEventArray[1] = This->m_thTimerInfo.hPlaybackTimerEvent;
  5242. if( hEventArray[0] == NULL || hEventArray[1] == NULL )
  5243. {
  5244. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to initialize events" );
  5245. This->HandleThreadError( DVERR_GENERIC );
  5246. goto EXIT_PLAYBACK;
  5247. }
  5248. if( This->m_audioPlaybackDevice->IsEmulated() )
  5249. {
  5250. DPFX(DPFPREP, DVF_WARNINGLEVEL, "WARNING: DirectSound is in emulated mode" );
  5251. dwAllowedLeadBuffers += DV_CLIENT_EMULATED_LEAD_ADJUST;
  5252. dwAllowedLeadBytes += DV_CLIENT_EMULATED_LEAD_ADJUST * This->m_dwUnCompressedFrameSize;
  5253. }
  5254. else
  5255. {
  5256. DPFX(DPFPREP, DVF_WARNINGLEVEL, "NOTE: DirectSound is in standard mode" );
  5257. }
  5258. while( 1 )
  5259. {
  5260. lWaitResult = WaitForMultipleObjects( 2, hEventArray, FALSE, INFINITE );
  5261. if( lWaitResult == WAIT_OBJECT_0 )
  5262. {
  5263. break;
  5264. }
  5265. hr = This->m_lpstGeneralBuffer->GetCurrentLead( &dwCurrentLead );
  5266. if( FAILED( hr ) )
  5267. {
  5268. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to get current position of main buff hr=0x%x", hr );
  5269. This->HandleThreadError( hr );
  5270. goto EXIT_PLAYBACK;
  5271. }
  5272. while( 1 )
  5273. {
  5274. if( dwCurrentLead < (dwAllowedLeadBytes) )
  5275. {
  5276. DPFX(DPFPREP, DVF_INFOLEVEL, "PWI, Lead: %d Running a pass..", dwCurrentLead );
  5277. }
  5278. else if( dwCurrentLead > dwHalfBufferSize )
  5279. {
  5280. DPFX(DPFPREP, DVF_INFOLEVEL, "PWI, Lead: %d Running a pass (wraparound)..", dwCurrentLead );
  5281. }
  5282. else
  5283. {
  5284. DPFX(DPFPREP, DVF_INFOLEVEL, "PWI, Lead: %d NOT Running a pass..", dwCurrentLead );
  5285. break;
  5286. }
  5287. DNEnterCriticalSection( &This->m_thTimerInfo.csPlayCount );
  5288. // Only run a max of two times per iteration
  5289. if( This->m_thTimerInfo.lPlaybackCount > dwAllowedLeadBuffers )
  5290. This->m_thTimerInfo.lPlaybackCount = dwAllowedLeadBuffers;
  5291. else
  5292. This->m_thTimerInfo.lPlaybackCount--;
  5293. DNLeaveCriticalSection( &This->m_thTimerInfo.csPlayCount );
  5294. #ifdef WINNT
  5295. fDesktopCurrent = ( USER_SHARED_DATA->ActiveConsoleId == NtCurrentPeb()->SessionId);
  5296. #endif
  5297. bHighPeak = 0;
  5298. // Update list
  5299. This->UpdateActivePlayPendingList( );
  5300. if( This->m_dvClientConfig.dwFlags & DVCLIENTCONFIG_ECHOSUPPRESSION )
  5301. {
  5302. DNEnterCriticalSection( &This->m_lockPlaybackMode );
  5303. dwEchoState = This->m_dwEchoState;
  5304. DNLeaveCriticalSection( &This->m_lockPlaybackMode );
  5305. }
  5306. else
  5307. {
  5308. dwEchoState = DVCECHOSTATE_IDLE;
  5309. }
  5310. if( dwEchoState != DVCECHOSTATE_RECORDING )
  5311. {
  5312. dwCurrentTime = GetTickCount();
  5313. pblSearch = This->m_blPlayActivePlayers.GetNext();
  5314. while( pblSearch != &This->m_blPlayActivePlayers )
  5315. {
  5316. pCurrentPlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blPlayList );
  5317. ASSERT_VPLAYER(pCurrentPlayer);
  5318. pblSearch = pblSearch->GetNext();
  5319. dwResultSize = This->m_dwUnCompressedFrameSize;
  5320. dwCompressStart = GetTickCount();
  5321. if( !pCurrentPlayer->IsInBoundConverterInitialized() )
  5322. {
  5323. hr = pCurrentPlayer->CreateInBoundConverter( This->m_lpdvfCompressionInfo->guidType, s_lpwfxPlaybackFormat );
  5324. if( FAILED( hr ) )
  5325. {
  5326. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to get create converter for player hr=0x%x", hr );
  5327. This->HandleThreadError( DVERR_GENERIC );
  5328. goto EXIT_PLAYBACK;
  5329. }
  5330. }
  5331. if( This->m_dvClientConfig.dwFlags & DVCLIENTCONFIG_PLAYBACKMUTE ||
  5332. !fDesktopCurrent )
  5333. {
  5334. frTmpFrame = pCurrentPlayer->Dequeue(&fLost, &fSilence);
  5335. frTmpFrame->Return();
  5336. continue;
  5337. }
  5338. hr = pCurrentPlayer->GetNextFrameAndDecompress( lpSourceBuffer, &dwResultSize, &fLost, &fSilence, &dwSeqNum, &dwMsgNum );
  5339. // If bad speech packet is constructed so it caused compressor to fail, it's ignored.
  5340. // Old code dropped us from the session, which would allow remote clients to drop
  5341. // us with a single packet.
  5342. if( FAILED( hr ) )
  5343. {
  5344. dwSeqNum = 0;
  5345. dwMsgNum = 0;
  5346. fLost = FALSE;
  5347. fSilence = TRUE;
  5348. }
  5349. dwCompressTime = GetTickCount() - dwCompressStart;
  5350. // STATSBLOCK: Begin
  5351. This->m_pStatsBlob->m_dwPDTTotal += dwCompressTime;
  5352. if( dwCompressTime < This->m_pStatsBlob->m_dwPDTMin )
  5353. {
  5354. This->m_pStatsBlob->m_dwPDTMin = dwCompressTime;
  5355. }
  5356. if( dwCompressTime > This->m_pStatsBlob->m_dwPDTMax )
  5357. {
  5358. This->m_pStatsBlob->m_dwPDTMax = dwCompressTime;
  5359. }
  5360. // STATSBLOCK: End
  5361. // STATSBLOCK: Begin
  5362. if( fLost )
  5363. {
  5364. DPFX(DPFPREP, DVF_CLIENT_SEQNUM_DEBUG_LEVEL, "SEQ: Dequeue: Lost Frame" );
  5365. DPFX(DPFPREP, DVF_GLITCH_DEBUG_LEVEL, "GLITCH: Dequeue: Packet was lost. Speech gap will occur." );
  5366. This->m_pStatsBlob->m_dwPPDQLost++;
  5367. }
  5368. else if( fSilence )
  5369. {
  5370. DPFX(DPFPREP, DVF_CLIENT_SEQNUM_DEBUG_LEVEL, "SEQ: Dequeue: Silent Frame" );
  5371. This->m_pStatsBlob->m_dwPPDQSilent++;
  5372. }
  5373. else
  5374. {
  5375. DPFX(DPFPREP, DVF_CLIENT_SEQNUM_DEBUG_LEVEL, "SEQ: Dequeue: Msg [%d] Seq [%d]", dwMsgNum, dwSeqNum );
  5376. This->m_pStatsBlob->m_dwPPDQSpeech++;
  5377. }
  5378. // STATSBLOCK: End
  5379. // If the player sent us silence, increment the silent count
  5380. if( fSilence )
  5381. {
  5382. // If we're receiving on this user
  5383. if( pCurrentPlayer->IsReceiving() )
  5384. {
  5385. // If it exceeds the max.
  5386. if( (dwCurrentTime - pCurrentPlayer->GetLastPlayback()) > PLAYBACK_RECEIVESTOP_TIMEOUT )
  5387. {
  5388. pCurrentPlayer->SetReceiving(FALSE);
  5389. dvMsgPlayerVoiceStop.dwSize = sizeof( dvMsgPlayerVoiceStop );
  5390. dvMsgPlayerVoiceStop.dvidSourcePlayerID = pCurrentPlayer->GetPlayerID();
  5391. dvMsgPlayerVoiceStop.pvPlayerContext = pCurrentPlayer->GetContext();
  5392. This->NotifyQueue_Add( DVMSGID_PLAYERVOICESTOP, &dvMsgPlayerVoiceStop, sizeof( dvMsgPlayerVoiceStop ) );
  5393. This->m_dwActiveCount--;
  5394. if( This->m_dwActiveCount == 0 )
  5395. {
  5396. if( This->m_dvClientConfig.dwFlags & DVCLIENTCONFIG_ECHOSUPPRESSION )
  5397. {
  5398. DNEnterCriticalSection( &This->m_lockPlaybackMode );
  5399. if( This->m_dwEchoState == DVCECHOSTATE_PLAYBACK )
  5400. {
  5401. DPFX(DPFPREP, PLAYBACK_SWITCH_DEBUG_LEVEL, "%%%% Switching to idle mode" );
  5402. This->m_dwEchoState = DVCECHOSTATE_IDLE;
  5403. }
  5404. DNLeaveCriticalSection( &This->m_lockPlaybackMode );
  5405. }
  5406. }
  5407. }
  5408. }
  5409. }
  5410. else
  5411. {
  5412. // We receive data and this is the first one
  5413. if( !pCurrentPlayer->IsReceiving() )
  5414. {
  5415. dvMsgPlayerVoiceStart.dvidSourcePlayerID = pCurrentPlayer->GetPlayerID();
  5416. dvMsgPlayerVoiceStart.pvPlayerContext = pCurrentPlayer->GetContext();
  5417. This->NotifyQueue_Add( DVMSGID_PLAYERVOICESTART, &dvMsgPlayerVoiceStart, sizeof(DVMSG_PLAYERVOICESTART) );
  5418. This->m_dwActiveCount++;
  5419. if( This->m_dwActiveCount == 1 )
  5420. {
  5421. if( This->m_dvClientConfig.dwFlags & DVCLIENTCONFIG_ECHOSUPPRESSION )
  5422. {
  5423. DNEnterCriticalSection( &This->m_lockPlaybackMode );
  5424. if( This->m_dwEchoState == DVCECHOSTATE_IDLE )
  5425. {
  5426. DPFX(DPFPREP, PLAYBACK_SWITCH_DEBUG_LEVEL, "%%%% Switching to playback mode" );
  5427. This->m_dwEchoState = DVCECHOSTATE_PLAYBACK;
  5428. }
  5429. DNLeaveCriticalSection( &This->m_lockPlaybackMode );
  5430. }
  5431. }
  5432. pCurrentPlayer->SetReceiving(TRUE);
  5433. }
  5434. }
  5435. fMixed = FALSE;
  5436. // If the frame was not silence, decompress it and then
  5437. // mix it into the mixer buffer
  5438. if( !fSilence &&
  5439. fDesktopCurrent &&
  5440. !(This->m_dvClientConfig.dwFlags & DVCLIENTCONFIG_PLAYBACKMUTE)
  5441. )
  5442. {
  5443. DPFX(DPFPREP, DVF_INFOLEVEL, "Player: 0x%x getting frame.. it's speech", pCurrentPlayer->GetPlayerID() );
  5444. // Lock the buffer list
  5445. DNEnterCriticalSection( &This->m_csBufferLock );
  5446. lpstCurrent = This->m_lpstBufferList;
  5447. if( pCurrentPlayer->GetLastPeak() > bHighPeak )
  5448. {
  5449. bHighPeak = pCurrentPlayer->GetLastPeak();
  5450. }
  5451. // Loop through list looking for buffers to mix into
  5452. while( lpstCurrent != NULL )
  5453. {
  5454. if( lpstCurrent->GetTarget() == pCurrentPlayer->GetPlayerID() )
  5455. {
  5456. lpstCurrent->MixInSingle( lpSourceBuffer );
  5457. fMixed = TRUE;
  5458. }
  5459. else if( This->m_lpSessionTransport->IsPlayerInGroup( lpstCurrent->GetTarget(), pCurrentPlayer->GetPlayerID() ) )
  5460. {
  5461. lpstCurrent->MixIn( lpSourceBuffer );
  5462. fMixed = TRUE;
  5463. }
  5464. lpstCurrent = lpstCurrent->m_lpstNext;
  5465. }
  5466. DNLeaveCriticalSection( &This->m_csBufferLock );
  5467. if( !(This->m_dvClientConfig.dwFlags & DVCLIENTCONFIG_MUTEGLOBAL) )
  5468. {
  5469. // If we didn't mix into any user created buffers, then
  5470. // Mix into the main buffer
  5471. if( !fMixed )
  5472. {
  5473. if( This->m_dwPlayActiveCount == 1 )
  5474. {
  5475. hr = This->m_lpstGeneralBuffer->MixInSingle( lpSourceBuffer );
  5476. }
  5477. else
  5478. {
  5479. hr = This->m_lpstGeneralBuffer->MixIn( lpSourceBuffer );
  5480. }
  5481. if( FAILED( hr ) )
  5482. {
  5483. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to do mix." );
  5484. DNLeaveCriticalSection( &This->m_csBufferLock );
  5485. This->HandleThreadError( hr );
  5486. goto EXIT_PLAYBACK;
  5487. }
  5488. }
  5489. }
  5490. }
  5491. else
  5492. {
  5493. DPFX(DPFPREP, DVF_INFOLEVEL, "Player: 0x%x getting frame.. it's silence", pCurrentPlayer->GetPlayerID() );
  5494. }
  5495. } // for each player
  5496. } // not in the recording state and at least one talking
  5497. else
  5498. {
  5499. bHighPeak = 0;
  5500. }
  5501. // Lock the buffer list
  5502. DNEnterCriticalSection( &This->m_csBufferLock );
  5503. lpstCurrent = This->m_lpstBufferList;
  5504. #if defined(DEBUG) || defined(DBG)
  5505. This->CHECKLISTINTEGRITY();
  5506. #endif
  5507. // Loop through list looking for buffers to mix into
  5508. while( lpstCurrent != NULL )
  5509. {
  5510. #if defined(DEBUG) || defined(DBG)
  5511. This->CHECKLISTINTEGRITY();
  5512. #endif
  5513. hr = lpstCurrent->Commit();
  5514. if( FAILED( hr ) )
  5515. {
  5516. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error commiting to buffer hr=0x%x -- Locked by user?", hr );
  5517. DNLeaveCriticalSection( &This->m_csBufferLock );
  5518. if( hr == DSERR_NODRIVER )
  5519. {
  5520. hr = DVERR_INVALIDDEVICE;
  5521. }
  5522. else
  5523. {
  5524. hr = DVERR_LOCKEDBUFFER;
  5525. }
  5526. This->HandleThreadError( hr );
  5527. goto EXIT_PLAYBACK;
  5528. }
  5529. lpstCurrent = lpstCurrent->m_lpstNext;
  5530. }
  5531. This->m_bLastPlaybackPeak = bHighPeak;
  5532. hr = This->m_lpstGeneralBuffer->Commit();
  5533. if( FAILED( hr ) )
  5534. {
  5535. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error commiting to buffer hr=0x%x -- Locked by user?", hr );
  5536. DNLeaveCriticalSection( &This->m_csBufferLock );
  5537. if( hr == DSERR_NODRIVER )
  5538. {
  5539. hr = DVERR_INVALIDDEVICE;
  5540. }
  5541. else
  5542. {
  5543. hr = DVERR_LOCKEDBUFFER;
  5544. }
  5545. This->HandleThreadError( hr );
  5546. goto EXIT_PLAYBACK;
  5547. }
  5548. DNLeaveCriticalSection( &This->m_csBufferLock );
  5549. hr = This->m_lpstGeneralBuffer->GetCurrentLead( &dwCurrentLead );
  5550. if( FAILED( hr ) )
  5551. {
  5552. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to get current pos of main buffer hr=0x%x", hr );
  5553. This->HandleThreadError( hr );
  5554. goto EXIT_PLAYBACK;
  5555. }
  5556. }
  5557. }
  5558. EXIT_PLAYBACK:
  5559. DPFX(DPFPREP, DVF_INFOLEVEL, "PT: Exiting" );
  5560. // Stop the playback
  5561. //This->m_audioPlaybackBuffer->Stop();
  5562. // Deallocate the buffers
  5563. DPFX(DPFPREP, DVF_INFOLEVEL, "PT: mixer gone" );
  5564. if( lpSourceBuffer != NULL )
  5565. {
  5566. delete [] lpSourceBuffer;
  5567. }
  5568. DPFX(DPFPREP, DVF_INFOLEVEL, "PT: source gone" );
  5569. // Signal that thread is done.
  5570. SetEvent( This->m_hPlaybackDone );
  5571. DPFX(DPFPREP, DVF_INFOLEVEL, "PT: Shutdown complete" );
  5572. COM_CoUninitialize();
  5573. _endthread();
  5574. }
  5575. #undef DPF_MODNAME
  5576. #define DPF_MODNAME "CDirectVoiceClientEngine::SetPlaybackVolume"
  5577. //
  5578. // SetPlaybackVolume
  5579. //
  5580. // Sets the playback volume of all the playback buffers
  5581. //
  5582. HRESULT CDirectVoiceClientEngine::SetPlaybackVolume( LONG lVolume )
  5583. {
  5584. CSoundTarget *lpstCurrent;
  5585. CAudioPlaybackBuffer *lpdsBuffer;
  5586. m_audioPlaybackBuffer->SetVolume( lVolume );
  5587. // Lock the buffer list
  5588. DNEnterCriticalSection( &m_csBufferLock );
  5589. lpstCurrent = m_lpstBufferList;
  5590. // Loop through list of buffers setting the volume
  5591. while( lpstCurrent != NULL )
  5592. {
  5593. lpdsBuffer = lpstCurrent->GetBuffer();
  5594. if( lpdsBuffer != NULL )
  5595. {
  5596. lpdsBuffer->SetVolume( lVolume );
  5597. }
  5598. lpstCurrent = lpstCurrent->m_lpstNext;
  5599. }
  5600. // Lock the buffer list
  5601. DNLeaveCriticalSection( &m_csBufferLock );
  5602. return DV_OK;
  5603. }
  5604. #undef DPF_MODNAME
  5605. #define DPF_MODNAME "CDirectVoiceClientEngine::HandleThreadError"
  5606. //
  5607. // HandleThreadError
  5608. //
  5609. // Handles errors within threads. When an error occurs this function sets the
  5610. // session lost flag and notifies the notifythread to perform a disconnect.
  5611. //
  5612. void CDirectVoiceClientEngine::HandleThreadError( HRESULT hrResult )
  5613. {
  5614. DoSessionLost( hrResult );
  5615. }
  5616. #undef DPF_MODNAME
  5617. #define DPF_MODNAME "CDirectVoiceClientEngine::RecordThread"
  5618. // RecordThread
  5619. //
  5620. // Thread to handle compression / transmission
  5621. //
  5622. void CDirectVoiceClientEngine::RecordThread( void *lpParam )
  5623. {
  5624. CDirectVoiceClientEngine *This = (CDirectVoiceClientEngine *) lpParam;
  5625. DNASSERT( This != NULL );
  5626. DPFX(DPFPREP, DVF_ENTRYLEVEL, "DVCE::RecordThread() Begin" );
  5627. HANDLE hEventArray[2];
  5628. LONG lWaitResult;
  5629. HRESULT hr;
  5630. BOOL fContinue = FALSE;
  5631. DWORD dwNumRunsPerWakeup = 0;
  5632. CClientRecordSubSystem *subSystem = NULL;
  5633. hr = COM_CoInitialize(NULL);
  5634. if( FAILED( hr ) )
  5635. {
  5636. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error innitializing COM on record thread" );
  5637. goto EXIT_ERROR;
  5638. }
  5639. subSystem = new CClientRecordSubSystem( This );
  5640. // Check that the converter is valid
  5641. if( subSystem == NULL )
  5642. {
  5643. DPFX(DPFPREP, DVF_ERRORLEVEL, "Memory alloc failure" );
  5644. goto EXIT_ERROR;
  5645. }
  5646. hr = subSystem->Initialize();
  5647. if( FAILED( hr ) )
  5648. {
  5649. DPFX(DPFPREP, DVF_ERRORLEVEL, "Record Sub Error during init hr=0x%x", hr );
  5650. goto EXIT_ERROR;
  5651. }
  5652. hEventArray[0] = This->m_hRecordTerminate;
  5653. hEventArray[1] = This->m_thTimerInfo.hRecordTimerEvent;
  5654. // Loop while we're connected
  5655. while( 1 )
  5656. {
  5657. lWaitResult = WaitForMultipleObjects( 2, hEventArray, FALSE, INFINITE );
  5658. This->m_pStatsBlob->m_recStats.m_dwNumWakeups++;
  5659. if( lWaitResult == WAIT_OBJECT_0 )
  5660. {
  5661. break;
  5662. }
  5663. fContinue = TRUE;
  5664. dwNumRunsPerWakeup = 0;
  5665. while( fContinue )
  5666. {
  5667. // Wait for next frame, if this fails recording
  5668. // system is locked. Return
  5669. hr = subSystem->GetNextFrame( &fContinue );
  5670. if( FAILED( hr ) )
  5671. {
  5672. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to retrieve next frame hr = 0x%x", hr );
  5673. goto EXIT_ERROR;
  5674. }
  5675. if( !fContinue )
  5676. {
  5677. ResetEvent( This->m_thTimerInfo.hRecordTimerEvent );
  5678. break;
  5679. }
  5680. This->m_pStatsBlob->m_recStats.m_dwRPWTotal++;
  5681. dwNumRunsPerWakeup++;
  5682. DPFX(DPFPREP, DVF_INFOLEVEL, "> RTAR: Got" );
  5683. hr = subSystem->RecordFSM();
  5684. if( FAILED( hr ) )
  5685. {
  5686. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to update FSM hr=0x%x", hr );
  5687. goto EXIT_ERROR;
  5688. }
  5689. DPFX(DPFPREP, DVF_INFOLEVEL, "> RTAR: FSM" );
  5690. // Transmit the frame if need be.
  5691. hr = subSystem->TransmitFrame();
  5692. if( FAILED( hr ) )
  5693. {
  5694. DPFX(DPFPREP, DVF_ERRORLEVEL, "TransmitFrame Failed hr=0x%x", hr );
  5695. goto EXIT_ERROR;
  5696. }
  5697. DPFX(DPFPREP, DVF_INFOLEVEL, "> RTAR: Trans" );
  5698. }
  5699. if( dwNumRunsPerWakeup < This->m_pStatsBlob->m_recStats.m_dwRPWMin )
  5700. {
  5701. This->m_pStatsBlob->m_recStats.m_dwRPWMin = dwNumRunsPerWakeup;
  5702. }
  5703. if( dwNumRunsPerWakeup > This->m_pStatsBlob->m_recStats.m_dwRPWMax )
  5704. {
  5705. This->m_pStatsBlob->m_recStats.m_dwRPWMax = dwNumRunsPerWakeup;
  5706. }
  5707. }
  5708. // Delete the recording subsystem
  5709. delete subSystem;
  5710. DPFX(DPFPREP, DVF_ENTRYLEVEL, "Record Sub Gone" );
  5711. COM_CoUninitialize();
  5712. SetEvent( This->m_hRecordDone );
  5713. DPFX(DPFPREP, DVF_ENTRYLEVEL, "Record Gone" );
  5714. return;
  5715. EXIT_ERROR:
  5716. if( subSystem )
  5717. delete subSystem;
  5718. This->HandleThreadError( DVERR_RECORDSYSTEMERROR );
  5719. COM_CoUninitialize();
  5720. SetEvent( This->m_hRecordDone );
  5721. _endthread();
  5722. }
  5723. #undef DPF_MODNAME
  5724. #define DPF_MODNAME "CDirectVoiceClientEngine::MigrateHost"
  5725. // MigrateHost
  5726. //
  5727. // This function is called by DV_HostMigrate when the local client has received a host
  5728. // migration notification.
  5729. //
  5730. //
  5731. HRESULT CDirectVoiceClientEngine::MigrateHost( DVID dvidNewHost, LPDIRECTPLAYVOICESERVER lpdvServer )
  5732. {
  5733. return DV_OK;
  5734. }
  5735. #undef DPF_MODNAME
  5736. #define DPF_MODNAME "CDirectVoiceClientEngine::NotifyQueue_Flush"
  5737. //
  5738. // NotifyQueue_Flush
  5739. //
  5740. // This function does not return until all notifications have been indicated
  5741. //
  5742. void CDirectVoiceClientEngine::NotifyQueue_Flush()
  5743. {
  5744. BFCSingleLock slLock( &m_csNotifyQueueLock );
  5745. slLock.Lock();
  5746. while( m_lpNotifyList )
  5747. {
  5748. NotifyQueue_IndicateNext();
  5749. }
  5750. }
  5751. #undef DPF_MODNAME
  5752. #define DPF_MODNAME "CDirectVoiceClientEngine::NotifyQueue_IndicateNext"
  5753. HRESULT CDirectVoiceClientEngine::NotifyQueue_IndicateNext()
  5754. {
  5755. HRESULT hr;
  5756. CNotifyElement *neElement;
  5757. hr = NotifyQueue_Get( &neElement );
  5758. if( FAILED( hr ) )
  5759. {
  5760. DPFX(DPFPREP, DVF_ERRORLEVEL, "NotifyQueue_Get Failed hr=0x%x", hr );
  5761. }
  5762. else
  5763. {
  5764. DPFX(DPFPREP, DVF_INFOLEVEL, "Sending notification type=0x%x", neElement->m_dwType );
  5765. if( neElement->m_etElementType == NOTIFY_FIXED )
  5766. {
  5767. TransmitMessage( neElement->m_dwType,
  5768. &neElement->m_element.fixed,
  5769. neElement->m_dwDataSize );
  5770. }
  5771. else
  5772. {
  5773. TransmitMessage( neElement->m_dwType,
  5774. neElement->m_element.dynamic.m_lpData,
  5775. neElement->m_dwDataSize );
  5776. }
  5777. DPFX(DPFPREP, DVF_INFOLEVEL, "Returning notification" );
  5778. NotifyQueue_ElementFree( neElement );
  5779. }
  5780. return hr;
  5781. }
  5782. #undef DPF_MODNAME
  5783. #define DPF_MODNAME "CDirectVoiceClientEngine::NotifyQueue_Add"
  5784. // Queue up a notification for the user
  5785. HRESULT CDirectVoiceClientEngine::NotifyQueue_Add( DWORD dwMessageType, LPVOID lpData, DWORD dwDataSize, PVOID pvContext, CNotifyElement::PNOTIFY_COMPLETE pNotifyFunc )
  5786. {
  5787. BFCSingleLock slLock( &m_csNotifyQueueLock );
  5788. slLock.Lock();
  5789. if( !m_fNotifyQueueEnabled )
  5790. {
  5791. DPFX(DPFPREP, DVF_WARNINGLEVEL, "Ignoring indication, queue disabled" );
  5792. }
  5793. CNotifyElement *lpNewElement;
  5794. HRESULT hr;
  5795. lpNewElement = (CNotifyElement *) m_fpNotifications.Get( );
  5796. if( lpNewElement == NULL )
  5797. {
  5798. DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to get a block for a notifier" );
  5799. return DVERR_OUTOFMEMORY;
  5800. }
  5801. // Setup the notification callbacks, if there are ones
  5802. lpNewElement->pvContext = pvContext;
  5803. lpNewElement->pNotifyFunc = pNotifyFunc;
  5804. if( dwMessageType == DVMSGID_PLAYERVOICESTOP )
  5805. {
  5806. const DVMSG_PLAYERVOICESTOP* pMsgStop = (PDVMSG_PLAYERVOICESTOP) lpData;
  5807. DNASSERT( pMsgStop->dwSize == sizeof( DVMSG_PLAYERVOICESTOP ) );
  5808. DNASSERT( dwDataSize == sizeof( DVMSG_PLAYERVOICESTOP ) );
  5809. }
  5810. if( dwMessageType == DVMSGID_PLAYERVOICESTART )
  5811. {
  5812. const DVMSG_PLAYERVOICESTART* pMsgStart = (PDVMSG_PLAYERVOICESTART) lpData;
  5813. DNASSERT( pMsgStart->dwSize == sizeof( DVMSG_PLAYERVOICESTART ) );
  5814. DNASSERT( dwDataSize == sizeof( DVMSG_PLAYERVOICESTART ) );
  5815. }
  5816. if( dwMessageType == DVMSGID_PLAYEROUTPUTLEVEL )
  5817. {
  5818. const DVMSG_PLAYEROUTPUTLEVEL* pMsgOutput = (PDVMSG_PLAYEROUTPUTLEVEL) lpData;
  5819. DNASSERT( pMsgOutput->dwSize == sizeof( DVMSG_PLAYEROUTPUTLEVEL ) );
  5820. DNASSERT( dwDataSize == sizeof( DVMSG_PLAYEROUTPUTLEVEL ) );
  5821. }
  5822. lpNewElement->m_lpNext = m_lpNotifyList;
  5823. lpNewElement->m_dwType = dwMessageType;
  5824. lpNewElement->m_dwDataSize = dwDataSize;
  5825. if( dwDataSize <= DV_CLIENT_NOTIFY_ELEMENT_SIZE )
  5826. {
  5827. lpNewElement->m_etElementType = NOTIFY_FIXED;
  5828. }
  5829. else
  5830. {
  5831. lpNewElement->m_etElementType = NOTIFY_DYNAMIC;
  5832. }
  5833. if( lpNewElement->m_etElementType == NOTIFY_FIXED )
  5834. {
  5835. memcpy( &lpNewElement->m_element.fixed, lpData, dwDataSize );
  5836. }
  5837. else if( lpNewElement->m_etElementType == NOTIFY_DYNAMIC )
  5838. {
  5839. lpNewElement->m_element.dynamic.m_lpData = new BYTE[dwDataSize];
  5840. if( lpNewElement->m_element.dynamic.m_lpData == NULL )
  5841. {
  5842. m_fpNotifications.Release( lpNewElement );
  5843. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to alloc memory for notification" );
  5844. return DVERR_OUTOFMEMORY;
  5845. }
  5846. memcpy( lpNewElement->m_element.dynamic.m_lpData, lpData, dwDataSize );
  5847. }
  5848. else
  5849. {
  5850. DNASSERT( FALSE );
  5851. }
  5852. // Fixups for internal pointers
  5853. //
  5854. // Required for certain message types (currently only DVMSGID_SETTARGETS)
  5855. if( dwMessageType == DVMSGID_SETTARGETS )
  5856. {
  5857. PDVMSG_SETTARGETS pdvSetTarget;
  5858. if( lpNewElement->m_etElementType == NOTIFY_FIXED )
  5859. {
  5860. pdvSetTarget = (PDVMSG_SETTARGETS) &lpNewElement->m_element.fixed;
  5861. }
  5862. else
  5863. {
  5864. pdvSetTarget = (PDVMSG_SETTARGETS) lpNewElement->m_element.dynamic.m_lpData;
  5865. }
  5866. pdvSetTarget->pdvidTargets = (PDVID) &pdvSetTarget[1];
  5867. lpNewElement->m_dwDataSize = sizeof( DVMSG_SETTARGETS );
  5868. }
  5869. // We're ignoring this notification, call the completion immediately.
  5870. if( !m_fNotifyQueueEnabled )
  5871. {
  5872. DPFX(DPFPREP, DVF_WARNINGLEVEL, "Ignoring notification, calling completion immediately" );
  5873. NotifyQueue_ElementFree( lpNewElement );
  5874. return DV_OK;
  5875. }
  5876. m_lpNotifyList = lpNewElement;
  5877. ReleaseSemaphore( m_hNewNotifyElement, 1, NULL );
  5878. return DV_OK;
  5879. }
  5880. #undef DPF_MODNAME
  5881. #define DPF_MODNAME "CDirectVoiceClientEngine::NotifyQueue_Get"
  5882. // Retrieve the next element in our queue.
  5883. //
  5884. // If there is an element, return DV_OK, otherwise DVERR_GENERIC.
  5885. //
  5886. HRESULT CDirectVoiceClientEngine::NotifyQueue_Get( CNotifyElement **pneElement )
  5887. {
  5888. BFCSingleLock slLock( &m_csNotifyQueueLock );
  5889. slLock.Lock();
  5890. CNotifyElement *lpIterator = m_lpNotifyList;
  5891. CNotifyElement *lpTrailIterator = NULL;
  5892. if( lpIterator == NULL )
  5893. return DVERR_GENERIC;
  5894. // Move forward to the last element in the list
  5895. while( lpIterator->m_lpNext != NULL )
  5896. {
  5897. lpTrailIterator = lpIterator;
  5898. lpIterator = lpIterator->m_lpNext;
  5899. }
  5900. *pneElement = lpIterator;
  5901. // Remove the last element in the list
  5902. // Special case, only element on the list
  5903. if( lpTrailIterator == NULL )
  5904. {
  5905. m_lpNotifyList = NULL;
  5906. }
  5907. else
  5908. {
  5909. lpTrailIterator->m_lpNext = NULL;
  5910. }
  5911. return DV_OK;
  5912. }
  5913. #undef DPF_MODNAME
  5914. #define DPF_MODNAME "CDirectVoiceClientEngine::NotifyQueue_Init"
  5915. HRESULT CDirectVoiceClientEngine::NotifyQueue_Init()
  5916. {
  5917. BFCSingleLock slLock( &m_csNotifyQueueLock );
  5918. slLock.Lock();
  5919. if (!m_fpNotifications.Initialize( sizeof( CNotifyElement ), NULL, NULL, NULL, NULL ))
  5920. {
  5921. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to create fixed pool for notify elements" );
  5922. return DVERR_OUTOFMEMORY;
  5923. }
  5924. m_lpNotifyList = NULL;
  5925. m_hNewNotifyElement = CreateSemaphore( NULL, 0, DVCLIENT_NOTIFY_MAXSEMCOUNT, NULL );
  5926. if (m_hNewNotifyElement==NULL)
  5927. {
  5928. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to create semaphore for notify elements" );
  5929. return DVERR_GENERIC;
  5930. }
  5931. m_fNotifyQueueEnabled = TRUE;
  5932. return DV_OK;
  5933. }
  5934. #undef DPF_MODNAME
  5935. #define DPF_MODNAME "CDirectVoiceClientEngine::NotifyQueue_Enable"
  5936. void CDirectVoiceClientEngine::NotifyQueue_Enable()
  5937. {
  5938. BFCSingleLock slLock( &m_csNotifyQueueLock );
  5939. slLock.Lock();
  5940. m_fNotifyQueueEnabled = TRUE;
  5941. }
  5942. #undef DPF_MODNAME
  5943. #define DPF_MODNAME "CDirectVoiceClientEngine::NotifyQueue_Disable"
  5944. void CDirectVoiceClientEngine::NotifyQueue_Disable()
  5945. {
  5946. BFCSingleLock slLock( &m_csNotifyQueueLock );
  5947. slLock.Lock();
  5948. m_fNotifyQueueEnabled = FALSE;
  5949. }
  5950. #undef DPF_MODNAME
  5951. #define DPF_MODNAME "CDirectVoiceClientEngine::NotifyQueue_ElementFree"
  5952. HRESULT CDirectVoiceClientEngine::NotifyQueue_ElementFree( CNotifyElement *lpElement )
  5953. {
  5954. // Call the notification function, if there is one
  5955. if( lpElement->pNotifyFunc )
  5956. {
  5957. (*lpElement->pNotifyFunc)(lpElement->pvContext,lpElement);
  5958. lpElement->pNotifyFunc = NULL;
  5959. lpElement->pvContext = NULL;
  5960. }
  5961. // If this element is dynamic free the associated memory
  5962. if( lpElement->m_etElementType == NOTIFY_DYNAMIC )
  5963. {
  5964. delete [] lpElement->m_element.dynamic.m_lpData;
  5965. }
  5966. // Return notifier to the fixed pool manager
  5967. m_fpNotifications.Release( lpElement );
  5968. return DV_OK;
  5969. }
  5970. #undef DPF_MODNAME
  5971. #define DPF_MODNAME "CDirectVoiceClientEngine::NotifyQueue_Free"
  5972. HRESULT CDirectVoiceClientEngine::NotifyQueue_Free()
  5973. {
  5974. BFCSingleLock slLock( &m_csNotifyQueueLock );
  5975. slLock.Lock();
  5976. CNotifyElement *lpTmpElement;
  5977. CNotifyElement *lpIteratorElement;
  5978. lpIteratorElement = m_lpNotifyList;
  5979. while( lpIteratorElement != NULL )
  5980. {
  5981. lpTmpElement = lpIteratorElement;
  5982. lpIteratorElement = lpIteratorElement->m_lpNext;
  5983. NotifyQueue_ElementFree( lpTmpElement );
  5984. }
  5985. if( m_hNewNotifyElement != NULL )
  5986. {
  5987. CloseHandle( m_hNewNotifyElement );
  5988. }
  5989. if (m_fNotifyQueueEnabled)
  5990. {
  5991. m_fpNotifications.DeInitialize();
  5992. }
  5993. return DV_OK;
  5994. }
  5995. //
  5996. // HANDLERS FOR WHEN WE HANDLE REMOTE SERVERS
  5997. //
  5998. //
  5999. #undef DPF_MODNAME
  6000. #define DPF_MODNAME "CDirectVoiceClientEngine::CreateGroup"
  6001. HRESULT CDirectVoiceClientEngine::CreateGroup( DVID dvID )
  6002. {
  6003. return S_OK;
  6004. }
  6005. #undef DPF_MODNAME
  6006. #define DPF_MODNAME "CDirectVoiceClientEngine::DeleteGroup"
  6007. HRESULT CDirectVoiceClientEngine::DeleteGroup( DVID dvID )
  6008. {
  6009. CheckForAndRemoveTarget( dvID );
  6010. // If there are any buffers for this player, delete them
  6011. //
  6012. // Leave the buffer around so the user can call Delete3DSoundBuffer
  6013. //
  6014. // DeleteSoundTarget( dvID );
  6015. return S_OK;
  6016. }
  6017. #undef DPF_MODNAME
  6018. #define DPF_MODNAME "CDirectVoiceClientEngine::AddPlayerToGroup"
  6019. HRESULT CDirectVoiceClientEngine::AddPlayerToGroup( DVID dvidGroup, DVID dvidPlayer )
  6020. {
  6021. return S_OK;
  6022. }
  6023. #undef DPF_MODNAME
  6024. #define DPF_MODNAME "CDirectVoiceClientEngine::RemovePlayerFromGroup"
  6025. HRESULT CDirectVoiceClientEngine::RemovePlayerFromGroup( DVID dvidGroup, DVID dvidPlayer )
  6026. {
  6027. return S_OK;
  6028. }
  6029. #undef DPF_MODNAME
  6030. #define DPF_MODNAME "CDirectVoiceClientEngine::DoSessionLost"
  6031. void CDirectVoiceClientEngine::DoSessionLost( HRESULT hrDisconnectResult )
  6032. {
  6033. if( GetCurrentState() == DVCSTATE_CONNECTED )
  6034. {
  6035. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "#### SESSION LOST [hr=0x%x]", hrDisconnectResult );
  6036. m_fSessionLost = TRUE;
  6037. DoSignalDisconnect( hrDisconnectResult );
  6038. }
  6039. else if( GetCurrentState() == DVCSTATE_DISCONNECTING )
  6040. {
  6041. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "#### Received session lost during disconnect, completing disconnect" );
  6042. DoSignalDisconnect( m_hrDisconnectResult );
  6043. }
  6044. else
  6045. {
  6046. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "#### Received session lost but not connected or disconnecting, ignoring" );
  6047. }
  6048. }
  6049. #undef DPF_MODNAME
  6050. #define DPF_MODNAME "CDirectVoiceClientEngine::DoSignalDisconnect"
  6051. void CDirectVoiceClientEngine::DoSignalDisconnect( HRESULT hrDisconnectResult )
  6052. {
  6053. DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "#### Disconnecting [hr=0x%x]", hrDisconnectResult );
  6054. m_hrDisconnectResult = hrDisconnectResult;
  6055. SetEvent( m_hNotifyDisconnect );
  6056. }
  6057. #undef DPF_MODNAME
  6058. #define DPF_MODNAME "CDirectVoiceClientEngine::ClientStats_Reset"
  6059. void CDirectVoiceClientEngine::ClientStats_Reset()
  6060. {
  6061. memset( &m_stats, 0x00, sizeof( ClientStatistics ) );
  6062. }
  6063. #undef DPF_MODNAME
  6064. #define DPF_MODNAME "CDirectVoiceClientEngine::ClientStats_Dump_Record"
  6065. void CDirectVoiceClientEngine::ClientStats_Dump_Record()
  6066. {
  6067. if( m_dvSoundDeviceConfig.dwFlags & DVSOUNDCONFIG_HALFDUPLEX )
  6068. {
  6069. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Session was half duplex, no record stats available" );
  6070. return;
  6071. }
  6072. char tmpBuffer[200];
  6073. DWORD dwRecRunLength = m_pStatsBlob->m_recStats.m_dwTimeStop - m_pStatsBlob->m_recStats.m_dwTimeStart;
  6074. if( dwRecRunLength == 0 )
  6075. dwRecRunLength = 1;
  6076. DWORD dwNumInternalRuns = m_pStatsBlob->m_recStats.m_dwNumWakeups;
  6077. if( dwNumInternalRuns == 0 )
  6078. dwNumInternalRuns = 1;
  6079. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Start Lag (ms) : %u", m_pStatsBlob->m_recStats.m_dwStartLag );
  6080. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Record Run Length (ms) : %u", dwRecRunLength );
  6081. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Speech Size (Uncomp.) : %u", m_pStatsBlob->m_recStats.m_dwUnCompressedSize );
  6082. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Frames / Buffer : %u", m_pStatsBlob->m_recStats.m_dwFramesPerBuffer );
  6083. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Time / Frame (ms) : %u", m_pStatsBlob->m_recStats.m_dwFrameTime );
  6084. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Silence Timeout : %u", m_pStatsBlob->m_recStats.m_dwSilenceTimeout );
  6085. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "# of wakeups : %u", m_pStatsBlob->m_recStats.m_dwNumWakeups );
  6086. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Runs / Wakeup : Avg: %u [%u..%u]",
  6087. m_pStatsBlob->m_recStats.m_dwRPWTotal / dwNumInternalRuns,
  6088. m_pStatsBlob->m_recStats.m_dwRPWMin,
  6089. m_pStatsBlob->m_recStats.m_dwRPWMax );
  6090. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "# of messages : %u", m_pStatsBlob->m_recStats.m_dwNumMessages );
  6091. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Sent Frames : %u", m_pStatsBlob->m_recStats.m_dwSentFrames );
  6092. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Ignored Frames : %u", m_pStatsBlob->m_recStats.m_dwIgnoredFrames );
  6093. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Message Length (frames): Avg: %u [%u..%u]",
  6094. (m_pStatsBlob->m_recStats.m_dwNumMessages == 0) ? 0 : m_pStatsBlob->m_recStats.m_dwMLTotal / m_pStatsBlob->m_recStats.m_dwNumMessages,
  6095. m_pStatsBlob->m_recStats.m_dwMLMin,
  6096. m_pStatsBlob->m_recStats.m_dwMLMax );
  6097. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Header Size (bytes) : Avg: %u [%u..%u]",
  6098. (m_pStatsBlob->m_recStats.m_dwSentFrames == 0) ? 0 : m_pStatsBlob->m_recStats.m_dwHSTotal / m_pStatsBlob->m_recStats.m_dwSentFrames,
  6099. m_pStatsBlob->m_recStats.m_dwHSMin,
  6100. m_pStatsBlob->m_recStats.m_dwHSMax );
  6101. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Speech Size (bytes) : Avg: %u [%u..%u]",
  6102. (m_pStatsBlob->m_recStats.m_dwSentFrames == 0) ? 0 : m_pStatsBlob->m_recStats.m_dwCSTotal / m_pStatsBlob->m_recStats.m_dwSentFrames,
  6103. m_pStatsBlob->m_recStats.m_dwCSMin,
  6104. m_pStatsBlob->m_recStats.m_dwCSMax );
  6105. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Speech Convert (ms) : Avg: %u [%u..%u]",
  6106. (m_pStatsBlob->m_recStats.m_dwSentFrames == 0) ? 0 : (m_pStatsBlob->m_recStats.m_dwCTTotal / m_pStatsBlob->m_recStats.m_dwSentFrames),
  6107. m_pStatsBlob->m_recStats.m_dwCTMin,
  6108. m_pStatsBlob->m_recStats.m_dwCTMax );
  6109. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Record Resets : %u [Before Success %u..%u]", m_pStatsBlob->m_recStats.m_dwRRTotal,
  6110. m_pStatsBlob->m_recStats.m_dwRRMin,
  6111. m_pStatsBlob->m_recStats.m_dwRRMax );
  6112. sprintf( tmpBuffer, "Rec Movement (ms) : Avg: %u [%u..%u]",
  6113. m_pStatsBlob->m_recStats.m_dwRMMSTotal / dwNumInternalRuns,
  6114. m_pStatsBlob->m_recStats.m_dwRMMSMin,
  6115. m_pStatsBlob->m_recStats.m_dwRMMSMax );
  6116. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
  6117. sprintf( tmpBuffer, "Rec Movement (frames) : Avg: %.2f [%.2f..%.2f]",
  6118. ( ((float)m_pStatsBlob->m_recStats.m_dwRMMSTotal) / ((float) dwNumInternalRuns)) / ((float) m_pStatsBlob->m_recStats.m_dwFrameTime),
  6119. ((float) m_pStatsBlob->m_recStats.m_dwRMMSMin) / ((float) m_pStatsBlob->m_recStats.m_dwFrameTime),
  6120. ((float) m_pStatsBlob->m_recStats.m_dwRMMSMax) / ((float) m_pStatsBlob->m_recStats.m_dwFrameTime));
  6121. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
  6122. sprintf( tmpBuffer, "Rec Movement (bytes) : Avg: %u [%u..%u]",
  6123. m_pStatsBlob->m_recStats.m_dwRMBTotal / dwNumInternalRuns,
  6124. m_pStatsBlob->m_recStats.m_dwRMBMin,
  6125. m_pStatsBlob->m_recStats.m_dwRMBMax );
  6126. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
  6127. sprintf( tmpBuffer, "Rec Movement (frames) : Avg: %.2f [%.2f..%.2f]",
  6128. (((float) m_pStatsBlob->m_recStats.m_dwRMBTotal) / ((float) dwNumInternalRuns)) / ((float) m_pStatsBlob->m_recStats.m_dwUnCompressedSize),
  6129. ((float) m_pStatsBlob->m_recStats.m_dwRMBMin) / ((float) m_pStatsBlob->m_recStats.m_dwUnCompressedSize),
  6130. ((float) m_pStatsBlob->m_recStats.m_dwRMBMax) / ((float) m_pStatsBlob->m_recStats.m_dwUnCompressedSize) );
  6131. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
  6132. sprintf( tmpBuffer, "Move Delta (ms) : Avg: %u [%u..%u]",
  6133. m_pStatsBlob->m_recStats.m_dwRTSLMTotal / dwNumInternalRuns,
  6134. m_pStatsBlob->m_recStats.m_dwRTSLMMin,
  6135. m_pStatsBlob->m_recStats.m_dwRTSLMMax );
  6136. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
  6137. sprintf( tmpBuffer, "Move Delta (frames) : Avg: %.2f [%.2f..%.2f]",
  6138. (((float) m_pStatsBlob->m_recStats.m_dwRTSLMTotal) / ((float) dwNumInternalRuns)) / ((float) m_pStatsBlob->m_recStats.m_dwUnCompressedSize),
  6139. ((float) m_pStatsBlob->m_recStats.m_dwRTSLMMin) / ((float) m_pStatsBlob->m_recStats.m_dwUnCompressedSize),
  6140. ((float) m_pStatsBlob->m_recStats.m_dwRTSLMMax) / ((float) m_pStatsBlob->m_recStats.m_dwUnCompressedSize) );
  6141. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
  6142. sprintf( tmpBuffer, "Record Lag (bytes) : Avg: %u [%u..%u]",
  6143. m_pStatsBlob->m_recStats.m_dwRLTotal / dwNumInternalRuns,
  6144. m_pStatsBlob->m_recStats.m_dwRLMin ,
  6145. m_pStatsBlob->m_recStats.m_dwRLMax );
  6146. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
  6147. sprintf( tmpBuffer, "Record Lag (frames) : Avg: %.2f [%.2f..%.2f]",
  6148. (float) ((float) m_pStatsBlob->m_recStats.m_dwRLTotal / (float) dwNumInternalRuns) / ((float) m_pStatsBlob->m_recStats.m_dwUnCompressedSize),
  6149. (float) ((float) m_pStatsBlob->m_recStats.m_dwRLMin) / (float) m_pStatsBlob->m_recStats.m_dwUnCompressedSize,
  6150. (float) ((float) m_pStatsBlob->m_recStats.m_dwRLMax) / (float) m_pStatsBlob->m_recStats.m_dwUnCompressedSize );
  6151. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, tmpBuffer );
  6152. }
  6153. #undef DPF_MODNAME
  6154. #define DPF_MODNAME "CDirectVoiceClientEngine::ClientStats_Dump_Playback"
  6155. void CDirectVoiceClientEngine::ClientStats_Dump_Playback()
  6156. {
  6157. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Playback Stats: " );
  6158. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Frames (silent) : %d", m_pStatsBlob->m_dwPPDQSilent );
  6159. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Frames (lost) : %d", m_pStatsBlob->m_dwPPDQLost );
  6160. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Frames (speech) : %d", m_pStatsBlob->m_dwPPDQSpeech );
  6161. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Speech Decompress (ms) : Avg: %u [%u..%u]",
  6162. (m_pStatsBlob->m_dwPPDQSpeech == 0) ? 0 : (m_pStatsBlob->m_dwPDTTotal / m_pStatsBlob->m_dwPPDQSpeech),
  6163. m_pStatsBlob->m_dwPDTMin,
  6164. m_pStatsBlob->m_dwPDTMax );
  6165. }
  6166. #undef DPF_MODNAME
  6167. #define DPF_MODNAME "CDirectVoiceClientEngine::ClientStats_Dump_Receive"
  6168. void CDirectVoiceClientEngine::ClientStats_Dump_Receive()
  6169. {
  6170. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Receive Stats: " );
  6171. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Received Frames : %d", m_pStatsBlob->m_dwPRESpeech );
  6172. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "Received But Lost : %d", m_pStatsBlob->m_dwPRESpeech - m_pStatsBlob->m_dwPPDQSpeech );
  6173. }
  6174. #undef DPF_MODNAME
  6175. #define DPF_MODNAME "CDirectVoiceClientEngine::ClientStats_Dump_Transmit"
  6176. void CDirectVoiceClientEngine::ClientStats_Dump_Transmit()
  6177. {
  6178. }
  6179. #undef DPF_MODNAME
  6180. #define DPF_MODNAME "CDirectVoiceClientEngine::ClientStats_Dump"
  6181. void CDirectVoiceClientEngine::ClientStats_Dump()
  6182. {
  6183. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "STATS DUMP: ----------------------------------------------------[Begin] " );
  6184. ClientStats_Dump_Record();
  6185. ClientStats_Dump_Playback();
  6186. ClientStats_Dump_Receive();
  6187. ClientStats_Dump_Transmit();
  6188. DPFX(DPFPREP, DVF_STATS_DEBUG_LEVEL, "STATS DUMP: ------------------------------------------------------[End] " );
  6189. }
  6190. #undef DPF_MODNAME
  6191. #define DPF_MODNAME "CDirectVoiceClientEngine::ClientStats_Begin"
  6192. void CDirectVoiceClientEngine::ClientStats_Begin()
  6193. {
  6194. m_pStatsBlob->m_dwTimeStart = GetTickCount();
  6195. m_pStatsBlob->m_dwMaxBuffers = 1;
  6196. m_pStatsBlob->m_dwTotalBuffers = 1;
  6197. m_pStatsBlob->m_dwPDTMin = 0xFFFFFFFF;
  6198. }
  6199. #undef DPF_MODNAME
  6200. #define DPF_MODNAME "CDirectVoiceClientEngine::ClientStats_End"
  6201. void CDirectVoiceClientEngine::ClientStats_End()
  6202. {
  6203. m_pStatsBlob->m_dwTimeStop = GetTickCount();
  6204. }
  6205. #undef DPF_MODNAME
  6206. #define DPF_MODNAME "CDirectVoiceClientEngine::ClientBufferAlloc"
  6207. PVOID CDirectVoiceClientEngine::ClientBufferAlloc( void *const pv, const DWORD dwSize )
  6208. {
  6209. return new BYTE[dwSize];
  6210. }
  6211. #undef DPF_MODNAME
  6212. #define DPF_MODNAME "CDirectVoiceClientEngine::ClientBufferFree"
  6213. void CDirectVoiceClientEngine::ClientBufferFree( void *const pv, void *const pvBuffer )
  6214. {
  6215. delete pvBuffer;
  6216. }
  6217. #undef DPF_MODNAME
  6218. /*#define DPF_MODNAME "CDirectVoiceClientEngine::GetTransmitBuffer"
  6219. PDVTRANSPORT_BUFFERDESC CDirectVoiceClientEngine::GetTransmitBuffer( DWORD dwSize, LPVOID *ppvSendContext )
  6220. {
  6221. PDVTRANSPORT_BUFFERDESC pNewBuffer;
  6222. pNewBuffer = new DVTRANSPORT_BUFFERDESC;
  6223. pNewBuffer->pBufferData = new BYTE[dwSize];
  6224. pNewBuffer->dwBufferSize = dwSize;
  6225. pNewBuffer->lRefCount = 0;
  6226. pNewBuffer->dwObjectType = DVTRANSPORT_OBJECTTYPE_CLIENT;
  6227. pNewBuffer->dwFlags = 0;
  6228. *ppvSendContext = pNewBuffer;
  6229. return pNewBuffer;
  6230. }
  6231. #undef DPF_MODNAME
  6232. #define DPF_MODNAME "CDirectVoiceClientEngine::ReturnTransmitBuffer"
  6233. // ReturnTransmitBuffer
  6234. //
  6235. // PDVTRANSPORT_BUFFERDESC pBufferDesc - Buffer description of buffer to return
  6236. // LPVOID lpvContext - Context value to be used when returning the buffer
  6237. //
  6238. void CDirectVoiceClientEngine::ReturnTransmitBuffer( PVOID pvContext )
  6239. {
  6240. PDVTRANSPORT_BUFFERDESC pBufferDesc = (PDVTRANSPORT_BUFFERDESC) pvContext;
  6241. delete [] pBufferDesc->pBufferData;
  6242. delete pBufferDesc;
  6243. }
  6244. #undef DPF_MODNAME
  6245. #define DPF_MODNAME "CDirectVoiceClientEngine::SendComplete"
  6246. HRESULT CDirectVoiceClientEngine::SendComplete( PDVEVENTMSG_SENDCOMPLETE pSendComplete )
  6247. {
  6248. ReturnTransmitBuffer( pSendComplete->pvUserContext );
  6249. return DV_OK;
  6250. }*/
  6251. #undef DPF_MODNAME
  6252. #define DPF_MODNAME "CDirectVoiceClientEngine::GetTransmitBuffer"
  6253. PDVTRANSPORT_BUFFERDESC CDirectVoiceClientEngine::GetTransmitBuffer( DWORD dwSize, LPVOID *ppvSendContext )
  6254. {
  6255. PDVTRANSPORT_BUFFERDESC pNewBuffer = NULL;
  6256. DWORD dwFPMIndex = 0xFFFFFFFF;
  6257. DWORD dwWastedSpace = 0xFFFFFFFF;
  6258. DWORD dwSearchFPMIndex = 0;
  6259. DNEnterCriticalSection( &m_csTransmitBufferLock );
  6260. pNewBuffer = (PDVTRANSPORT_BUFFERDESC) m_BufferDescPool.Get( );
  6261. DNLeaveCriticalSection( &m_csTransmitBufferLock );
  6262. DPFX(DPFPREP, DVF_BUFFERDESC_DEBUG_LEVEL, "BUFFERDESC: Got a buffer desc address 0x%p", (void *) pNewBuffer );
  6263. if( pNewBuffer == NULL )
  6264. {
  6265. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error getting transmit buffer" );
  6266. goto GETTRANSMITBUFFER_ERROR;
  6267. }
  6268. pNewBuffer->lRefCount = 0;
  6269. pNewBuffer->dwObjectType = DVTRANSPORT_OBJECTTYPE_CLIENT;
  6270. pNewBuffer->dwFlags = 0;
  6271. pNewBuffer->pBufferData = NULL;
  6272. for( dwSearchFPMIndex = 0; dwSearchFPMIndex < m_dwNumPools; dwSearchFPMIndex++ )
  6273. {
  6274. // Potential pool
  6275. if( m_pdwBufferPoolSizes[dwSearchFPMIndex] >= dwSize )
  6276. {
  6277. if( m_pdwBufferPoolSizes[dwSearchFPMIndex] - dwSize < dwWastedSpace )
  6278. {
  6279. dwWastedSpace = m_pdwBufferPoolSizes[dwSearchFPMIndex] - dwSize;
  6280. dwFPMIndex = dwSearchFPMIndex;
  6281. }
  6282. }
  6283. }
  6284. if( dwFPMIndex == 0xFFFFFFFF )
  6285. {
  6286. DNASSERT( FALSE );
  6287. DPFX(DPFPREP, 0, "Could not find pool large enough for buffer" );
  6288. goto GETTRANSMITBUFFER_ERROR;
  6289. }
  6290. pNewBuffer->pvContext = &m_pBufferPools[dwFPMIndex];
  6291. DNEnterCriticalSection( &m_csTransmitBufferLock );
  6292. pNewBuffer->pBufferData = (PBYTE) m_pBufferPools[dwFPMIndex].Get();
  6293. DNLeaveCriticalSection( &m_csTransmitBufferLock );
  6294. DPFX(DPFPREP, DVF_BUFFERDESC_DEBUG_LEVEL, "BUFFERDESC: Got a buffer value at address 0x%p", (void *) pNewBuffer->pBufferData );
  6295. DPFX(DPFPREP, DVF_BUFFERDESC_DEBUG_LEVEL, "BUFFERDESC: nInUse = %i", m_BufferDescPool.GetInUseCount() );
  6296. if( pNewBuffer->pBufferData == NULL )
  6297. {
  6298. DPFX(DPFPREP, 0, "Error getting buffer for buffer desc" );
  6299. goto GETTRANSMITBUFFER_ERROR;
  6300. }
  6301. pNewBuffer->dwBufferSize = dwSize;
  6302. *ppvSendContext = pNewBuffer;
  6303. return pNewBuffer;
  6304. GETTRANSMITBUFFER_ERROR:
  6305. DNEnterCriticalSection( &m_csTransmitBufferLock );
  6306. if( pNewBuffer != NULL && pNewBuffer->pBufferData != NULL )
  6307. {
  6308. ((CFixedPool*) pNewBuffer->pvContext)->Release( pNewBuffer->pBufferData );
  6309. }
  6310. if( pNewBuffer != NULL )
  6311. {
  6312. m_BufferDescPool.Release( pNewBuffer );
  6313. }
  6314. DNLeaveCriticalSection( &m_csTransmitBufferLock );
  6315. return NULL;
  6316. }
  6317. #undef DPF_MODNAME
  6318. #define DPF_MODNAME "CDirectVoiceClientEngine::ReturnTransmitBuffer"
  6319. // ReturnTransmitBuffer
  6320. //
  6321. // PDVTRANSPORT_BUFFERDESC pBufferDesc - Buffer description of buffer to return
  6322. // LPVOID lpvContext - Context value to be used when returning the buffer
  6323. //
  6324. void CDirectVoiceClientEngine::ReturnTransmitBuffer( PVOID pvContext )
  6325. {
  6326. PDVTRANSPORT_BUFFERDESC pBufferDesc = (PDVTRANSPORT_BUFFERDESC) pvContext;
  6327. CFixedPool* pPool = (CFixedPool*) pBufferDesc->pvContext;
  6328. DPFX(DPFPREP, DVF_BUFFERDESC_DEBUG_LEVEL, "BUFFERDESC: Returning a buffer desc at address 0x%p", (void *) pBufferDesc );
  6329. DPFX(DPFPREP, DVF_BUFFERDESC_DEBUG_LEVEL, "BUFFERDESC: Returning a buffer at address 0x%p", (void *) pBufferDesc->pBufferData );
  6330. DNEnterCriticalSection( &m_csTransmitBufferLock );
  6331. // Release memory
  6332. pPool->Release( pBufferDesc->pBufferData );
  6333. // Release buffer description
  6334. m_BufferDescPool.Release( pvContext );
  6335. DPFX(DPFPREP, DVF_BUFFERDESC_DEBUG_LEVEL, "BUFFERDESC: nInUse = %i", m_BufferDescPool.GetInUseCount() );
  6336. DNLeaveCriticalSection( &m_csTransmitBufferLock );
  6337. }
  6338. #undef DPF_MODNAME
  6339. #define DPF_MODNAME "CDirectVoiceClientEngine::SendComplete"
  6340. HRESULT CDirectVoiceClientEngine::SendComplete( PDVEVENTMSG_SENDCOMPLETE pSendComplete )
  6341. {
  6342. ReturnTransmitBuffer( pSendComplete->pvUserContext );
  6343. return DV_OK;
  6344. }
  6345. #undef DPF_MODNAME
  6346. #define DPF_MODNAME "CDirectVoiceClientEngine::SetupInitialBuffers"
  6347. // SetupBuffersInitial
  6348. //
  6349. // This function sets up the first transmit buffers which do not vary
  6350. // in size w/the compression type.
  6351. //
  6352. HRESULT CDirectVoiceClientEngine::SetupInitialBuffers()
  6353. {
  6354. HRESULT hr = DV_OK;
  6355. DWORD dwIndex;
  6356. m_dwNumPools = CLIENT_POOLS_NUM;
  6357. if (!m_BufferDescPool.Initialize( sizeof(DVTRANSPORT_BUFFERDESC), NULL, NULL, NULL, NULL ))
  6358. {
  6359. DPFX(DPFPREP, 0, "Error allocating memory" );
  6360. hr = DVERR_OUTOFMEMORY;
  6361. goto SETUPBUFFERS_ERROR;
  6362. }
  6363. m_pBufferPools = new CFixedPool[m_dwNumPools];
  6364. if( m_pBufferPools == NULL )
  6365. {
  6366. DPFX(DPFPREP, 0, "Error allocating memory" );
  6367. hr = DVERR_OUTOFMEMORY;
  6368. goto SETUPBUFFERS_ERROR;
  6369. }
  6370. m_pdwBufferPoolSizes = new DWORD[m_dwNumPools];
  6371. if( m_pdwBufferPoolSizes == NULL )
  6372. {
  6373. DPFX(DPFPREP, 0, "Error allocating memory" );
  6374. hr = DVERR_OUTOFMEMORY;
  6375. goto SETUPBUFFERS_ERROR;
  6376. }
  6377. //This is broken if the number of client pools is ever adjusted from default
  6378. //value of 3. Since that's an unlikely change just compile time assert for now
  6379. DBG_CASSERT(CLIENT_POOLS_NUM==3);
  6380. m_pdwBufferPoolSizes[0] = CLIENT_POOLS_SIZE_MESSAGE;
  6381. m_pdwBufferPoolSizes[1] = CLIENT_POOLS_SIZE_PLAYERLIST;
  6382. m_pdwBufferPoolSizes[ m_dwNumPools-1] = 0;
  6383. for( dwIndex = 0; dwIndex < m_dwNumPools-1; dwIndex++ )
  6384. {
  6385. if (!m_pBufferPools[dwIndex].Initialize( m_pdwBufferPoolSizes[dwIndex], NULL, NULL, NULL, NULL ))
  6386. {
  6387. DPFX(DPFPREP, 0, "Error creating transmit buffers" );
  6388. hr=DVERR_OUTOFMEMORY;
  6389. goto SETUPBUFFERS_ERROR;
  6390. }
  6391. }
  6392. return DV_OK;
  6393. SETUPBUFFERS_ERROR:
  6394. FreeBuffers();
  6395. return hr;
  6396. }
  6397. #undef DPF_MODNAME
  6398. #define DPF_MODNAME "CDirectVoiceServerEngine::SetupSpeechBuffer"
  6399. // SetupSpeechBuffer
  6400. //
  6401. // This function sets up the buffer pool for speech sends, whose size will
  6402. // depend on the compression type. Must be done after we know CT but
  6403. // before we do first speech transmission.
  6404. //
  6405. HRESULT CDirectVoiceClientEngine::SetupSpeechBuffer()
  6406. {
  6407. if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_PEER ||
  6408. m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_ECHO )
  6409. {
  6410. m_pdwBufferPoolSizes[m_dwNumPools-1] = sizeof( DVPROTOCOLMSG_SPEECHHEADER )+m_dwCompressedFrameSize+COMPRESSION_SLUSH;
  6411. }
  6412. else
  6413. {
  6414. m_pdwBufferPoolSizes[m_dwNumPools-1] = sizeof( DVPROTOCOLMSG_SPEECHWITHTARGET ) + m_dwCompressedFrameSize +
  6415. (sizeof( DVID )*CLIENT_POOLS_NUM_TARGETS_BUFFERED)+COMPRESSION_SLUSH;
  6416. }
  6417. if (!m_pBufferPools[m_dwNumPools-1].Initialize( m_pdwBufferPoolSizes[m_dwNumPools-1], NULL, NULL, NULL, NULL ))
  6418. {
  6419. DPFX(DPFPREP, 0, "Error creating transmit buffers" );
  6420. return DVERR_OUTOFMEMORY;
  6421. }
  6422. return DV_OK;
  6423. }
  6424. #undef DPF_MODNAME
  6425. #define DPF_MODNAME "CDirectVoiceServerEngine::FreeBuffers"
  6426. HRESULT CDirectVoiceClientEngine::FreeBuffers()
  6427. {
  6428. DWORD dwIndex;
  6429. if( m_pBufferPools != NULL )
  6430. {
  6431. for( dwIndex = 0; dwIndex < m_dwNumPools; dwIndex++ )
  6432. {
  6433. if (m_pdwBufferPoolSizes[dwIndex])
  6434. m_pBufferPools[dwIndex].DeInitialize( );
  6435. }
  6436. delete [] m_pBufferPools;
  6437. m_pBufferPools = NULL;
  6438. }
  6439. if( m_pdwBufferPoolSizes != NULL )
  6440. {
  6441. delete [] m_pdwBufferPoolSizes;
  6442. m_pdwBufferPoolSizes = 0;
  6443. }
  6444. m_BufferDescPool.DeInitialize( );
  6445. m_dwNumPools = 0;
  6446. return DV_OK;
  6447. }
  6448. #undef DPF_MODNAME
  6449. #define DPF_MODNAME "CDirectVoiceClientEngine::ValidateSessionType"
  6450. BOOL CDirectVoiceClientEngine::ValidateSessionType( DWORD dwSessionType )
  6451. {
  6452. return (dwSessionType > 0 && dwSessionType < DVSESSIONTYPE_MAX);
  6453. }
  6454. #undef DPF_MODNAME
  6455. #define DPF_MODNAME "CDirectVoiceClientEngine::ValidateSessionFlags"
  6456. BOOL CDirectVoiceClientEngine::ValidateSessionFlags( DWORD dwFlags )
  6457. {
  6458. return (dwFlags < DVSESSION_MAX);
  6459. }
  6460. #undef DPF_MODNAME
  6461. #define DPF_MODNAME "CDirectVoiceClientEngine::ValidatePlayerFlags"
  6462. // Only allow player flags that specify either a 0 or the half duplex flags
  6463. BOOL CDirectVoiceClientEngine::ValidatePlayerFlags(DWORD dwFlags)
  6464. {
  6465. return (dwFlags == DVPLAYERCAPS_HALFDUPLEX || dwFlags == 0);
  6466. }
  6467. #undef DPF_MODNAME
  6468. #define DPF_MODNAME "CDirectVoiceClientEngine::ValidatePlayerDVID"
  6469. BOOL CDirectVoiceClientEngine::ValidatePlayerDVID(DVID dvid)
  6470. {
  6471. return (dvid != DVID_ALLPLAYERS);
  6472. }
  6473. #undef DPF_MODNAME
  6474. #define DPF_MODNAME "CDirectVoiceClientEngine::ValidatePacketType"
  6475. BOOL CDirectVoiceClientEngine::ValidatePacketType( const DVPROTOCOLMSG_FULLMESSAGE* lpdvFullMessage ) const
  6476. {
  6477. switch( lpdvFullMessage->dvGeneric.dwType )
  6478. {
  6479. case DVMSGID_HOSTMIGRATELEAVE:
  6480. case DVMSGID_HOSTMIGRATED:
  6481. case DVMSGID_CREATEVOICEPLAYER:
  6482. case DVMSGID_DELETEVOICEPLAYER:
  6483. case DVMSGID_PLAYERLIST:
  6484. return ( ( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_PEER ) );
  6485. break;
  6486. case DVMSGID_SPEECHWITHFROM:
  6487. return ( ( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_FORWARDING) );
  6488. break;
  6489. case DVMSGID_SETTARGETS:
  6490. return ( m_dvSessionDesc.dwFlags & DVSESSION_SERVERCONTROLTARGET );
  6491. break;
  6492. }
  6493. return TRUE;
  6494. }