missing project/build files
[client-tools.git] / src / external / 3rd / library / vivoxSharedWrapper / Vivox.cpp
blobbf2239474d49bc1dbb7175f2e7705032adeb4c0e
1 ////////////////////////////////////////////////////////////////////////////////
2 //Vivox.cpp
3 //
4 //03/11/08 Joshua M. Kriegshauser
5 //
6 //Copyright (c) 2008 Sony Online Entertainment, LLC. All rights reserved.
7 ////////////////////////////////////////////////////////////////////////////////
9 #include "Vivox.h"
11 #include <algorithm>
12 #include <functional>
14 // Disable the nameless struct warning.
15 #pragma warning(disable:4201)
17 #define WIN32_EXTRA_LEAN
18 #define WIN32_LEAN_AND_MEAN
19 #include <windows.h>
20 #include <mmsystem.h>
21 #include <stdio.h>
23 // Vivox includes
24 #define VIVOX_TYPES_ONLY 1 // We're importing from DLL, so only need the types
25 #include "Vxc.h"
26 #include "VxcRequests.h"
27 #include "VxcResponses.h"
28 #include "VxcEvents.h"
30 // Functions from DLL
31 HMODULE shVivoxDll = 0;
32 #define DECLARE_FN( ret, name, parms ) name##_type name = 0
33 DECLARE_FN(void,destroy_resp,(vx_resp_base_t *pCmd));
34 DECLARE_FN(void,destroy_evt,(vx_evt_base_t *pCmd));
35 DECLARE_FN(int,vx_get_message,(vx_message_base_t** message));
36 DECLARE_FN(int,vx_issue_request,(vx_req_base_t* request));
37 DECLARE_FN(char*,vx_strdup,(const char*));
38 DECLARE_FN(void,vx_req_connector_initiate_shutdown_create,(vx_req_connector_initiate_shutdown_t ** req));
39 DECLARE_FN(void,vx_req_account_logout_create,(vx_req_account_logout_t ** req));
40 DECLARE_FN(void,vx_req_connector_create_create,(vx_req_connector_create_t ** req));
41 DECLARE_FN(void,vx_req_account_login_create,(vx_req_account_login_t ** req));
42 DECLARE_FN(void,vx_req_account_channel_add_moderator_create,(vx_req_account_channel_add_moderator ** req));
43 DECLARE_FN(void,vx_req_account_channel_remove_moderator_create,(vx_req_account_channel_remove_moderator ** req));
44 DECLARE_FN(void,vx_req_channel_ban_user_create,(vx_req_channel_ban_user ** req));
45 DECLARE_FN(void,vx_req_channel_kick_user_create,(vx_req_channel_kick_user ** req));
46 DECLARE_FN(void,vx_req_channel_mute_user_create,(vx_req_channel_mute_user ** req));
47 DECLARE_FN(void,vx_req_channel_mute_all_users_create,(vx_req_channel_mute_all_users ** req));
48 DECLARE_FN(void,vx_req_account_channel_update_create,(vx_req_account_channel_update ** req));
49 DECLARE_FN(void,vx_req_account_channel_get_moderators_create,(vx_req_account_channel_get_moderators ** req));
50 DECLARE_FN(void,vx_req_sessiongroup_create_create,(vx_req_sessiongroup_create_t ** req));
51 DECLARE_FN(void,vx_req_sessiongroup_add_session_create,(vx_req_sessiongroup_add_session_t ** req));
52 DECLARE_FN(void,vx_req_sessiongroup_remove_session_create,(vx_req_sessiongroup_remove_session_t ** req));
53 DECLARE_FN(void,vx_req_sessiongroup_terminate_create,(vx_req_sessiongroup_terminate_t ** req));
54 DECLARE_FN(void,vx_req_session_create_create,(vx_req_session_create_t ** req));
55 DECLARE_FN(void,vx_req_session_terminate_create,(vx_req_session_terminate_t ** req));
56 DECLARE_FN(void,vx_req_session_media_disconnect_create,(vx_req_session_media_disconnect ** req));
57 DECLARE_FN(void,vx_req_session_send_message_create,(vx_req_session_send_message ** req));
58 DECLARE_FN(void,vx_req_connector_mute_local_mic_create,(vx_req_connector_mute_local_mic ** req));
59 DECLARE_FN(void,vx_req_connector_mute_local_speaker_create,(vx_req_connector_mute_local_speaker ** req));
60 DECLARE_FN(void,vx_req_connector_set_local_mic_volume_create,(vx_req_connector_set_local_mic_volume ** req));
61 DECLARE_FN(void,vx_req_connector_set_local_speaker_volume_create,(vx_req_connector_set_local_speaker_volume ** req));
62 DECLARE_FN(void,vx_req_session_set_participant_mute_for_me_create,(vx_req_session_set_participant_mute_for_me ** req));
63 DECLARE_FN(void,vx_req_session_set_participant_volume_for_me_create,(vx_req_session_set_participant_volume_for_me ** req));
64 DECLARE_FN(void,vx_req_aux_get_render_devices_create,(vx_req_aux_get_render_devices ** req));
65 DECLARE_FN(void,vx_req_aux_set_render_device_create,(vx_req_aux_set_render_device ** req));
66 DECLARE_FN(void,vx_req_aux_get_capture_devices_create,(vx_req_aux_get_capture_devices ** req));
67 DECLARE_FN(void,vx_req_aux_set_capture_device_create,(vx_req_aux_set_capture_device ** req));
68 DECLARE_FN(void,vx_req_aux_start_buffer_capture_create,(vx_req_aux_start_buffer_capture ** req));
69 DECLARE_FN(void,vx_req_aux_play_audio_buffer_create,(vx_req_aux_play_audio_buffer ** req));
70 DECLARE_FN(void,vx_req_aux_render_audio_stop_create,(vx_req_aux_render_audio_stop ** req));
71 DECLARE_FN(void,vx_req_session_set_3d_position_create,(vx_req_session_set_3d_position ** req));
72 DECLARE_FN(void,vx_req_sessiongroup_set_tx_session_create,(vx_req_sessiongroup_set_tx_session ** req));
73 DECLARE_FN(void,vx_req_aux_global_monitor_keyboard_mouse_create,(vx_req_aux_global_monitor_keyboard_mouse_t ** req));
74 DECLARE_FN(void,vx_req_session_mute_local_speaker_create,(vx_req_session_mute_local_speaker ** req));
75 DECLARE_FN(void,vx_req_session_set_local_speaker_volume_create,(vx_req_session_set_local_speaker_volume ** req));
76 DECLARE_FN(void,vx_req_session_send_notification_create,(vx_req_session_send_notification ** req));
77 DECLARE_FN(void,vx_req_aux_capture_audio_start_create,(vx_req_aux_capture_audio_start ** req));
78 DECLARE_FN(void,vx_req_aux_capture_audio_stop_create,(vx_req_aux_capture_audio_stop ** req));
79 DECLARE_FN(void,vx_req_aux_set_mic_level_create,(vx_req_aux_set_mic_level ** req));
80 DECLARE_FN(void,vx_req_aux_set_speaker_level_create,(vx_req_aux_set_speaker_level ** req));
81 DECLARE_FN(void,vx_on_application_exit,());
82 #if VIVOX_VERSION >= 3
83 DECLARE_FN(void,vx_req_aux_diagnostic_state_dump_create,(vx_req_aux_diagnostic_state_dump ** req));
84 DECLARE_FN(int, vx_alloc_sdk_handle,(const char *address, unsigned short port, VX_SDK_HANDLE *handle));
85 DECLARE_FN(int, vx_free_sdk_handle,(VX_SDK_HANDLE sdkHandle));
86 #endif
87 #undef DECLARE_FN
89 static bool sLoadAttempted = false;
91 void
92 sUnloadVivoxDLL()
94 // Unload appears to crash randomly in versions prior to 3, even if calling vx_on_application_exit()
95 #if VIVOX_VERSION >= 3
96 if ( shVivoxDll )
98 // Call onExit function if retrieved
99 if ( vx_on_application_exit )
101 vx_on_application_exit();
104 sLoadAttempted = false;
105 FreeLibrary( shVivoxDll );
106 shVivoxDll = 0;
107 #define CLEAR_FN(name) name = 0
108 CLEAR_FN(destroy_resp);
109 CLEAR_FN(destroy_evt);
110 CLEAR_FN(vx_get_message);
111 CLEAR_FN(vx_issue_request);
112 CLEAR_FN(vx_strdup);
113 CLEAR_FN(vx_req_connector_initiate_shutdown_create);
114 CLEAR_FN(vx_req_account_logout_create);
115 CLEAR_FN(vx_req_connector_create_create);
116 CLEAR_FN(vx_req_account_login_create);
117 CLEAR_FN(vx_req_account_channel_add_moderator_create);
118 CLEAR_FN(vx_req_account_channel_remove_moderator_create);
119 CLEAR_FN(vx_req_channel_ban_user_create);
120 CLEAR_FN(vx_req_channel_kick_user_create);
121 CLEAR_FN(vx_req_channel_mute_user_create);
122 CLEAR_FN(vx_req_channel_mute_all_users_create);
123 CLEAR_FN(vx_req_account_channel_update_create);
124 CLEAR_FN(vx_req_account_channel_get_moderators_create);
125 CLEAR_FN(vx_req_sessiongroup_create_create);
126 CLEAR_FN(vx_req_sessiongroup_add_session_create);
127 CLEAR_FN(vx_req_sessiongroup_remove_session_create);
128 CLEAR_FN(vx_req_sessiongroup_terminate_create);
129 CLEAR_FN(vx_req_session_create_create);
130 CLEAR_FN(vx_req_session_terminate_create);
131 CLEAR_FN(vx_req_session_media_disconnect_create);
132 CLEAR_FN(vx_req_session_send_message_create);
133 CLEAR_FN(vx_req_connector_mute_local_mic_create);
134 CLEAR_FN(vx_req_connector_mute_local_speaker_create);
135 CLEAR_FN(vx_req_connector_set_local_mic_volume_create);
136 CLEAR_FN(vx_req_connector_set_local_speaker_volume_create);
137 CLEAR_FN(vx_req_session_set_participant_mute_for_me_create);
138 CLEAR_FN(vx_req_session_set_participant_volume_for_me_create);
139 CLEAR_FN(vx_req_aux_get_render_devices_create);
140 CLEAR_FN(vx_req_aux_set_render_device_create);
141 CLEAR_FN(vx_req_aux_get_capture_devices_create);
142 CLEAR_FN(vx_req_aux_set_capture_device_create);
143 CLEAR_FN(vx_req_aux_start_buffer_capture_create);
144 CLEAR_FN(vx_req_aux_play_audio_buffer_create);
145 CLEAR_FN(vx_req_aux_render_audio_stop_create);
146 CLEAR_FN(vx_req_session_set_3d_position_create);
147 CLEAR_FN(vx_req_sessiongroup_set_tx_session_create);
148 CLEAR_FN(vx_req_aux_global_monitor_keyboard_mouse_create);
149 CLEAR_FN(vx_req_session_mute_local_speaker_create);
150 CLEAR_FN(vx_req_session_set_local_speaker_volume_create);
151 CLEAR_FN(vx_req_session_send_notification_create);
152 CLEAR_FN(vx_on_application_exit);
153 CLEAR_FN(vx_req_aux_capture_audio_start_create);
154 CLEAR_FN(vx_req_aux_capture_audio_stop_create);
155 CLEAR_FN(vx_req_aux_set_mic_level_create);
156 CLEAR_FN(vx_req_aux_set_speaker_level_create);
157 #if VIVOX_VERSION >= 3
158 CLEAR_FN(vx_req_aux_diagnostic_state_dump_create);
159 CLEAR_FN(vx_alloc_sdk_handle);
160 CLEAR_FN(vx_free_sdk_handle);
161 #endif
162 #undef CLEAR_FN
164 #endif
167 bool
168 sStartService( const char* sExe, const char* /*sIP*/, int log_level )
170 // If the management process is available, start it and connect to it.
171 HANDLE hFile = ::CreateFile( sExe, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 );
172 if ( hFile != INVALID_HANDLE_VALUE )
174 // File exists, start the process and set the ENV variable
175 ::CloseHandle( hFile );
177 STARTUPINFO startupInfo;
178 memset( &startupInfo, 0, sizeof(startupInfo) );
179 startupInfo.cb = sizeof(STARTUPINFO);
181 PROCESS_INFORMATION processInfo;
183 // Command line options:
184 // -ll <n> log level
185 // -lf <folder> log folder
186 // -lp <prefix> prefix for log files
187 // -ls <suffix> suffix for log files
189 char commandLine[ 1024 ];
190 int pos = _snprintf( commandLine, sizeof(commandLine), "%s -lf logs -lp vvs -ls .log", sExe );
192 if ( log_level > 0 )
194 pos += _snprintf( commandLine + pos, sizeof(commandLine) - pos, " -ll %d", log_level );
197 commandLine[ pos ] = '\0';
199 if ( TRUE == ::CreateProcess( 0, commandLine, 0, 0, FALSE, 0, 0, 0, &startupInfo, &processInfo ) )
201 return true;
205 return false;
208 bool
209 sLoadVivoxDLL( void (*onLoadFunc)( void ), void (*onWarning)( const char* file, int line, const char* pattern, ... ) )
211 if ( shVivoxDll )
212 return true;
214 if ( sLoadAttempted )
216 // Something is broken, don't try loading again
217 return false;
219 sLoadAttempted = true;
221 shVivoxDll = LoadLibraryA( "vivoxsdk.dll" );
222 if ( 0 == shVivoxDll )
224 // alert( "Failed to load vivoxsdk.dll" );
225 return false;
228 // Import functions
229 #define IMPORT_FN(name) name = (name##_type)GetProcAddress( shVivoxDll, #name ); if ( 0 == name ) { onWarning( __FILE__, __LINE__, "Failed to import function: %s", #name ); sUnloadVivoxDLL(); return false; }
230 IMPORT_FN(vx_on_application_exit); // Import application exit function first
231 IMPORT_FN(destroy_resp);
232 IMPORT_FN(destroy_evt);
233 IMPORT_FN(vx_get_message);
234 IMPORT_FN(vx_issue_request);
235 IMPORT_FN(vx_strdup);
236 IMPORT_FN(vx_req_connector_initiate_shutdown_create);
237 IMPORT_FN(vx_req_account_logout_create);
238 IMPORT_FN(vx_req_connector_create_create);
239 IMPORT_FN(vx_req_account_login_create);
240 IMPORT_FN(vx_req_account_channel_add_moderator_create);
241 IMPORT_FN(vx_req_account_channel_remove_moderator_create);
242 IMPORT_FN(vx_req_channel_ban_user_create);
243 IMPORT_FN(vx_req_channel_kick_user_create);
244 IMPORT_FN(vx_req_channel_mute_user_create);
245 IMPORT_FN(vx_req_channel_mute_all_users_create);
246 IMPORT_FN(vx_req_account_channel_update_create);
247 IMPORT_FN(vx_req_account_channel_get_moderators_create);
248 IMPORT_FN(vx_req_sessiongroup_create_create);
249 IMPORT_FN(vx_req_sessiongroup_add_session_create);
250 IMPORT_FN(vx_req_sessiongroup_remove_session_create);
251 IMPORT_FN(vx_req_sessiongroup_terminate_create);
252 IMPORT_FN(vx_req_session_create_create);
253 IMPORT_FN(vx_req_session_terminate_create);
254 IMPORT_FN(vx_req_session_media_disconnect_create);
255 IMPORT_FN(vx_req_session_send_message_create);
256 IMPORT_FN(vx_req_connector_mute_local_mic_create);
257 IMPORT_FN(vx_req_connector_mute_local_speaker_create);
258 IMPORT_FN(vx_req_connector_set_local_mic_volume_create);
259 IMPORT_FN(vx_req_connector_set_local_speaker_volume_create);
260 IMPORT_FN(vx_req_session_set_participant_mute_for_me_create);
261 IMPORT_FN(vx_req_session_set_participant_volume_for_me_create);
262 IMPORT_FN(vx_req_aux_get_render_devices_create);
263 IMPORT_FN(vx_req_aux_set_render_device_create);
264 IMPORT_FN(vx_req_aux_get_capture_devices_create);
265 IMPORT_FN(vx_req_aux_set_capture_device_create);
266 IMPORT_FN(vx_req_aux_start_buffer_capture_create);
267 IMPORT_FN(vx_req_aux_play_audio_buffer_create);
268 IMPORT_FN(vx_req_aux_render_audio_stop_create);
269 IMPORT_FN(vx_req_session_set_3d_position_create);
270 IMPORT_FN(vx_req_sessiongroup_set_tx_session_create);
271 IMPORT_FN(vx_req_aux_global_monitor_keyboard_mouse_create);
272 IMPORT_FN(vx_req_session_mute_local_speaker_create);
273 IMPORT_FN(vx_req_session_set_local_speaker_volume_create);
274 IMPORT_FN(vx_req_session_send_notification_create);
275 IMPORT_FN(vx_req_aux_capture_audio_start_create);
276 IMPORT_FN(vx_req_aux_capture_audio_stop_create);
277 IMPORT_FN(vx_req_aux_set_mic_level_create);
278 IMPORT_FN(vx_req_aux_set_speaker_level_create);
279 #if VIVOX_VERSION >= 3
280 IMPORT_FN(vx_req_aux_diagnostic_state_dump_create);
281 IMPORT_FN(vx_alloc_sdk_handle);
282 IMPORT_FN(vx_free_sdk_handle);
283 #endif
284 #undef IMPORT_FN
286 // Alright, we just loaded the DLL, now do all of the initial state setup
287 onLoadFunc();
289 return true;
293 bool
294 sGetKeyState( int iKeyCode )
296 // Swap mouse buttons if necessary (see docs for GetAsyncKeyState)
297 if ( ( iKeyCode == VK_LBUTTON || iKeyCode == VK_RBUTTON ) && GetSystemMetrics( SM_SWAPBUTTON ) == TRUE )
299 if ( iKeyCode == VK_LBUTTON )
300 iKeyCode = VK_RBUTTON;
301 else
302 iKeyCode = VK_LBUTTON;
305 // For numeric keypad keys, verify that numlock is on
306 const bool kbNumeric = ( iKeyCode >= VK_NUMPAD0 && iKeyCode <= VK_NUMPAD9 );
307 if ( kbNumeric )
309 SHORT s = GetKeyState( VK_NUMLOCK );
310 if ( ( s & 0x0001 ) == 0 )
312 // If the numeric key is still 'on', force it to be off
313 s = GetKeyState( iKeyCode );
314 if ( ( s & 0x8000 ) != 0 )
316 BYTE keys[ 256 ];
317 if ( GetKeyboardState( keys ) == TRUE )
319 keys[ iKeyCode ] = 0;
320 SetKeyboardState( keys );
323 return false;
327 SHORT s = kbNumeric ? GetKeyState( iKeyCode ) : GetAsyncKeyState( iKeyCode );
328 bool b = ( s & 0x8000 ) != 0;
330 if ( !b )
332 // Handle generic control/alt/shift
333 if ( iKeyCode == VK_CONTROL )
334 b = ( ( GetAsyncKeyState( VK_LCONTROL ) | GetAsyncKeyState( VK_RCONTROL ) ) & 0x8000 ) != 0;
335 else if ( iKeyCode == VK_MENU )
336 b = ( ( GetAsyncKeyState( VK_LMENU ) | GetAsyncKeyState( VK_RMENU ) ) & 0x8000 ) != 0;
337 else if ( iKeyCode == VK_SHIFT )
338 b = ( ( GetAsyncKeyState( VK_LSHIFT ) | GetAsyncKeyState( VK_RSHIFT ) ) & 0x8000 ) != 0;
341 return b;
345 VivoxCheckMic
346 sCheckMic( const std::string& sDevice, bool bFix /*=false*/ )
348 // Disable for Vista
349 static enum
351 UNKNOWN = -1,
352 IS_NOT_VISTA = 0,
353 IS_VISTA
354 } sIsVista = UNKNOWN;
356 if ( UNKNOWN == sIsVista )
358 // Determine once
359 OSVERSIONINFOEX info;
360 info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
361 ::GetVersionEx( (OSVERSIONINFO*)&info );
362 const bool kbIsVista = ( 6 == info.dwMajorVersion &&
363 0 == info.dwMinorVersion &&
364 VER_NT_WORKSTATION == info.wProductType );
366 sIsVista = kbIsVista ? IS_VISTA : IS_NOT_VISTA;
369 if ( IS_VISTA == sIsVista )
371 return VCM_OK;
374 int retval = VCM_OK;
376 const UINT num = mixerGetNumDevs();
378 bool bFound = false;
379 for ( UINT uDev = 0; uDev != num; ++uDev )
381 MIXERCAPS caps;
382 if ( mixerGetDevCaps( uDev, &caps, sizeof(caps) ) != MMSYSERR_NOERROR )
384 retval |= VCM_ERROR;
385 continue;
388 // Skip this device if it's not the one we're looking for
389 if ( !sDevice.empty() && sDevice != caps.szPname )
390 continue;
392 bFound = true;
394 HMIXER h;
395 if ( mixerOpen( &h, uDev, 0, 0, MIXER_OBJECTF_MIXER ) != MMSYSERR_NOERROR )
397 retval |= VCM_ERROR;
398 continue;
401 // Temp object to close the mixer device when we go out of scope (or continue;)
402 class MixerCloser
404 HMIXER& m_h;
405 public:
406 MixerCloser( HMIXER& h ) : m_h( h ) {}
407 ~MixerCloser() { mixerClose( m_h ); m_h = 0; }
409 private:
410 MixerCloser & operator= (const MixerCloser &) { return *this; }
411 } closer( h );
413 MIXERLINE lineWaveIn;
414 lineWaveIn.cbStruct = sizeof(lineWaveIn);
415 lineWaveIn.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
417 if ( mixerGetLineInfo( (HMIXEROBJ)h, &lineWaveIn, MIXER_GETLINEINFOF_COMPONENTTYPE ) != MMSYSERR_NOERROR )
419 retval |= VCM_ERROR;
420 continue;
423 DWORD dwDesiredLineID = ~0U;
425 // Check the Wave In mixer device for Mute, Volume and MUX controls
426 if ( lineWaveIn.cControls != 0 )
428 // Find the Mute All control (if it exists)
429 MIXERCONTROL mxc;
430 MIXERLINECONTROLS mxlc;
431 memset( &mxlc, 0, sizeof(mxlc) );
432 mxlc.cbStruct = sizeof(mxlc);
433 mxlc.cbmxctrl = sizeof(mxc);
434 mxlc.cControls = 1;
435 mxlc.pamxctrl = &mxc;
436 mxlc.dwLineID = lineWaveIn.dwLineID;
437 mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
438 if ( mixerGetLineControls( (HMIXEROBJ)h, &mxlc, MIXER_GETLINECONTROLSF_ONEBYTYPE ) == MMSYSERR_NOERROR )
440 // Found a Mute control. Query it and set it if necessary.
441 MIXERCONTROLDETAILS_BOOLEAN mxcdBool;
442 MIXERCONTROLDETAILS mxcd;
443 mxcd.cbStruct = sizeof(mxcd);
444 mxcd.dwControlID = mxc.dwControlID;
445 mxcd.cChannels = 1;
446 mxcd.cMultipleItems = 0;
447 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
448 mxcd.paDetails = &mxcdBool;
449 if ( mixerGetControlDetails( (HMIXEROBJ)h, &mxcd, MIXER_GETCONTROLDETAILSF_VALUE ) == MMSYSERR_NOERROR )
451 if ( mxcdBool.fValue == TRUE )
453 retval |= VCM_MUTED;
454 if ( bFix )
456 mxcdBool.fValue = FALSE;
457 if ( mixerSetControlDetails( (HMIXEROBJ)h, &mxcd, MIXER_SETCONTROLDETAILSF_VALUE ) != MMSYSERR_NOERROR )
459 retval |= VCM_ERROR;
464 else
466 retval |= VCM_ERROR;
470 // Find the volume control (if it exists)
471 mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
472 if ( mixerGetLineControls( (HMIXEROBJ)h, &mxlc, MIXER_GETLINECONTROLSF_ONEBYTYPE ) == MMSYSERR_NOERROR )
474 // Found a Volume control. Query it and set it if necessary.
475 MIXERCONTROLDETAILS_UNSIGNED mxcdUns;
476 MIXERCONTROLDETAILS mxcd;
477 mxcd.cbStruct = sizeof(mxcd);
478 mxcd.dwControlID = mxc.dwControlID;
479 mxcd.cChannels = 1;
480 mxcd.cMultipleItems = 0;
481 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
482 mxcd.paDetails = &mxcdUns;
483 if ( mixerGetControlDetails( (HMIXEROBJ)h, &mxcd, MIXER_GETCONTROLDETAILSF_VALUE ) == MMSYSERR_NOERROR )
485 const float kfRange = static_cast< float >( mxc.Bounds.dwMaximum - mxc.Bounds.dwMinimum );
486 DWORD desired = static_cast< DWORD >( 0.5f * kfRange ) + mxc.Bounds.dwMinimum;
488 if ( mxcdUns.dwValue < desired )
490 retval |= VCM_VOLUME_TOO_LOW;
491 if ( bFix )
493 mxcdUns.dwValue = desired;
494 if ( mixerSetControlDetails( (HMIXEROBJ)h, &mxcd, MIXER_SETCONTROLDETAILSF_VALUE ) != MMSYSERR_NOERROR )
496 retval |= VCM_ERROR;
501 else
503 retval |= VCM_ERROR;
507 // Find the MUX control and determine if we have more than one device
508 mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MUX;
509 if ( mixerGetLineControls( (HMIXEROBJ)h, &mxlc, MIXER_GETLINECONTROLSF_ONEBYTYPE ) == MMSYSERR_NOERROR &&
510 mxc.cMultipleItems > 1 )
512 // Got a MUX control, now see what it's set to. We need to get the list because the MUX items
513 // might be sorted differently than the connections
515 std::vector< MIXERCONTROLDETAILS_LISTTEXT > aList( mxc.cMultipleItems );
516 MIXERCONTROLDETAILS mxcdList;
517 mxcdList.cbStruct = sizeof(mxcdList);
518 mxcdList.dwControlID = mxc.dwControlID;
519 mxcdList.cChannels = 1;
520 mxcdList.cMultipleItems = mxc.cMultipleItems;
521 mxcdList.cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXT);
522 mxcdList.paDetails = &aList[ 0 ];
524 std::vector< MIXERCONTROLDETAILS_BOOLEAN > aBool( mxc.cMultipleItems );
525 MIXERCONTROLDETAILS mxcdBool;
526 mxcdBool.cbStruct = sizeof(mxcdBool);
527 mxcdBool.dwControlID = mxc.dwControlID;
528 mxcdBool.cChannels = 1;
529 mxcdBool.cMultipleItems = mxc.cMultipleItems;
530 mxcdBool.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
531 mxcdBool.paDetails = &aBool[ 0 ];
533 if ( mixerGetControlDetails( (HMIXEROBJ)h, &mxcdList, MIXER_GETCONTROLDETAILSF_LISTTEXT ) == MMSYSERR_NOERROR &&
534 mixerGetControlDetails( (HMIXEROBJ)h, &mxcdBool, MIXER_GETCONTROLDETAILSF_VALUE ) == MMSYSERR_NOERROR )
536 // Whew, got all the control details we need for the MUX. See if it's set to the right value.
537 // We'll also change the values here, but we only send them back to the API if we're told to fix.
539 // We also must handle multiple microphones, as some devices have separate front/rear mics
541 // Loop through once to see if a mic is already selected
542 for ( unsigned int iVal = 0; iVal != mxc.cMultipleItems; ++iVal )
544 if ( MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE == aList[ iVal ].dwParam2 &&
545 aBool[ iVal ].fValue == TRUE )
547 // Found a mic that was selected
548 dwDesiredLineID = aList[ iVal ].dwParam1;
549 break;
553 // If we don't have a desired mic, take the first one that is connected
554 if ( ~0U == dwDesiredLineID )
556 for ( unsigned int iVal = 0; iVal != mxc.cMultipleItems; ++iVal )
558 if ( MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE == aList[ iVal ].dwParam2 )
560 MIXERLINE line;
561 line.cbStruct = sizeof(line);
562 line.dwLineID = aList[ iVal ].dwParam1;
563 if ( mixerGetLineInfo( (HMIXEROBJ)h, &line, MIXER_GETLINEINFOF_LINEID ) != MMSYSERR_NOERROR )
565 retval |= VCM_ERROR;
566 continue;
569 if ( ( line.fdwLine & MIXERLINE_LINEF_DISCONNECTED ) == 0 )
571 // This line appears to be connected (if the line even supports notification about that)
572 // Take this one.
573 dwDesiredLineID = aList[ iVal ].dwParam1;
574 break;
580 for ( unsigned int iVal = 0; iVal != mxc.cMultipleItems; ++iVal )
582 if ( MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE == aList[ iVal ].dwParam2 )
584 // Select this mic as the desired mic if we don't already have one
585 if ( dwDesiredLineID == ~0U )
587 dwDesiredLineID = aList[ iVal ].dwParam1;
590 // If this is the desired mic, make sure it's turned on
591 if ( dwDesiredLineID == aList[ iVal ].dwParam1 )
593 if ( aBool[ iVal ].fValue != TRUE )
594 retval |= VCM_NOT_SELECTED;
595 aBool[ iVal ].fValue = TRUE;
597 else
599 // Not desired mic, make sure it's turned off
600 aBool[ iVal ].fValue = FALSE;
603 else
605 // Set everything other than the mic to false
606 aBool[ iVal ].fValue = FALSE;
610 if ( bFix )
612 // If told to fix but no desired mic was found, show as an error.
613 if ( dwDesiredLineID == ~0U || mixerSetControlDetails( (HMIXEROBJ)h, &mxcdBool, MIXER_SETCONTROLDETAILSF_VALUE ) != MMSYSERR_NOERROR )
614 retval |= VCM_ERROR;
617 else
619 retval |= VCM_ERROR;
624 // Now find the Microphone connection and check it for Mic-specific Volume and Mute controls
625 for ( unsigned int iCon = 0; iCon != lineWaveIn.cConnections; ++iCon )
627 MIXERLINE line = lineWaveIn;
628 line.dwSource = iCon;
630 if ( mixerGetLineInfo( (HMIXEROBJ)h, &line, MIXER_GETLINEINFOF_SOURCE ) != MMSYSERR_NOERROR )
632 retval |= VCM_ERROR;
633 continue;
636 if ( MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE != line.dwComponentType )
637 continue;
639 // Only care about our desired line ID (if we have a desired line ID)
640 if ( dwDesiredLineID != ~0U && line.dwLineID != dwDesiredLineID )
641 continue;
643 if ( line.fdwLine & MIXERLINE_LINEF_DISCONNECTED )
645 retval |= VCM_DISCONNECTED;
646 // This situation can't be fixed in code (at least... I don't think so)
649 if ( 0 == line.cControls )
650 continue;
652 // Find the Mute control (if it exists)
653 MIXERCONTROL mxc;
654 MIXERLINECONTROLS mxlc;
655 memset( &mxlc, 0, sizeof(mxlc) );
656 mxlc.cbStruct = sizeof(mxlc);
657 mxlc.cbmxctrl = sizeof(mxc);
658 mxlc.cControls = 1;
659 mxlc.pamxctrl = &mxc;
660 mxlc.dwLineID = line.dwLineID;
661 mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
662 if ( mixerGetLineControls( (HMIXEROBJ)h, &mxlc, MIXER_GETLINECONTROLSF_ONEBYTYPE ) == MMSYSERR_NOERROR )
664 // Found a Mute control. Query it and set it if necessary.
665 MIXERCONTROLDETAILS_BOOLEAN mxcdBool;
666 MIXERCONTROLDETAILS mxcd;
667 mxcd.cbStruct = sizeof(mxcd);
668 mxcd.dwControlID = mxc.dwControlID;
669 mxcd.cChannels = 1;
670 mxcd.cMultipleItems = 0;
671 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
672 mxcd.paDetails = &mxcdBool;
673 if ( mixerGetControlDetails( (HMIXEROBJ)h, &mxcd, MIXER_GETCONTROLDETAILSF_VALUE ) == MMSYSERR_NOERROR )
675 if ( mxcdBool.fValue == TRUE )
677 retval |= VCM_MUTED;
678 if ( bFix )
680 mxcdBool.fValue = FALSE;
681 if ( mixerSetControlDetails( (HMIXEROBJ)h, &mxcd, MIXER_SETCONTROLDETAILSF_VALUE ) != MMSYSERR_NOERROR )
683 retval |= VCM_ERROR;
688 else
690 retval |= VCM_ERROR;
694 // Find the volume control (if it exists)
695 mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
696 if ( mixerGetLineControls( (HMIXEROBJ)h, &mxlc, MIXER_GETLINECONTROLSF_ONEBYTYPE ) == MMSYSERR_NOERROR )
698 // Found a Volume control. Query it and set it if necessary.
699 MIXERCONTROLDETAILS_UNSIGNED mxcdUns;
700 MIXERCONTROLDETAILS mxcd;
701 mxcd.cbStruct = sizeof(mxcd);
702 mxcd.dwControlID = mxc.dwControlID;
703 mxcd.cChannels = 1;
704 mxcd.cMultipleItems = 0;
705 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
706 mxcd.paDetails = &mxcdUns;
707 if ( mixerGetControlDetails( (HMIXEROBJ)h, &mxcd, MIXER_GETCONTROLDETAILSF_VALUE ) == MMSYSERR_NOERROR )
709 const float kfRange = static_cast< float >( mxc.Bounds.dwMaximum - mxc.Bounds.dwMinimum );
710 DWORD desired = static_cast< DWORD >( 0.5f * kfRange ) + mxc.Bounds.dwMinimum;
712 if ( mxcdUns.dwValue < desired )
714 retval |= VCM_VOLUME_TOO_LOW;
715 if ( bFix )
717 mxcdUns.dwValue = desired;
718 if ( mixerSetControlDetails( (HMIXEROBJ)h, &mxcd, MIXER_SETCONTROLDETAILSF_VALUE ) != MMSYSERR_NOERROR )
720 retval |= VCM_ERROR;
725 else
727 retval |= VCM_ERROR;
733 if ( !bFound )
734 retval |= VCM_ERROR;
736 return (VivoxCheckMic)retval;
739 static HANDLE shVivoxMutex = 0;
741 bool
742 sGrabVivoxSystemMutex()
744 #if VIVOX_VERSION < 3
745 if ( shVivoxMutex )
747 // Already have it
748 return true;
751 // Create/Open the named mutex
752 shVivoxMutex = ::CreateMutex( 0, TRUE, "{4355ce47-39f2-4f52-bd0e-f6e609b20f82}" );
753 if ( 0 == shVivoxMutex )
754 return false;
756 if ( ERROR_ALREADY_EXISTS == ::GetLastError() )
758 // Ok, it exists, but we weren't granted ownership
759 if ( WAIT_OBJECT_0 != ::WaitForSingleObject( shVivoxMutex, 0 ) )
761 // Failed to grab it, just release for now
762 ::CloseHandle( shVivoxMutex );
763 shVivoxMutex = 0;
764 return false;
767 #endif
768 // Grabbed it
769 return true;
772 bool
773 sReleaseVivoxSystemMutex()
775 #if VIVOX_VERSION < 3
776 if ( shVivoxMutex )
778 ::ReleaseMutex( shVivoxMutex );
779 ::CloseHandle( shVivoxMutex );
780 shVivoxMutex = 0;
781 return true;
784 return false;
785 #else
786 return true;
787 #endif