Team Fortress 2 Source Code as on 22/4/2020
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.

432 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "OptionsSubAudio.h"
  7. #include "cvarslider.h"
  8. #include "EngineInterface.h"
  9. #include "ModInfo.h"
  10. #include "vgui_controls/ComboBox.h"
  11. #include "vgui_controls/QueryBox.h"
  12. #include "CvarToggleCheckButton.h"
  13. #include "tier1/KeyValues.h"
  14. #include "tier1/convar.h"
  15. #include <vgui/IInput.h>
  16. #include <steam/steam_api.h>
  17. #include <tier1/strtools.h>
  18. // memdbgon must be the last include file in a .cpp file!!!
  19. #include "tier0/memdbgon.h"
  20. using namespace vgui;
  21. // This member is static so that the updated audio language can be referenced during shutdown
  22. char* COptionsSubAudio::m_pchUpdatedAudioLanguage = (char*)GetLanguageShortName( k_Lang_English );
  23. enum SoundQuality_e
  24. {
  25. SOUNDQUALITY_LOW,
  26. SOUNDQUALITY_MEDIUM,
  27. SOUNDQUALITY_HIGH,
  28. };
  29. //-----------------------------------------------------------------------------
  30. // Purpose: Constructor
  31. //-----------------------------------------------------------------------------
  32. COptionsSubAudio::COptionsSubAudio(vgui::Panel *parent) : PropertyPage(parent, NULL)
  33. {
  34. m_pSFXSlider = new CCvarSlider( this, "SFXSlider", "#GameUI_SoundEffectVolume", 0.0f, 1.0f, "volume" );
  35. m_pMusicSlider = new CCvarSlider( this, "MusicSlider", "#GameUI_MusicVolume", 0.0f, 1.0f, "Snd_MusicVolume" );
  36. m_pCloseCaptionCombo = new ComboBox( this, "CloseCaptionCheck", 6, false );
  37. m_pCloseCaptionCombo->AddItem( "#GameUI_NoClosedCaptions", NULL );
  38. m_pCloseCaptionCombo->AddItem( "#GameUI_SubtitlesAndSoundEffects", NULL );
  39. m_pCloseCaptionCombo->AddItem( "#GameUI_Subtitles", NULL );
  40. m_pSoundQualityCombo = new ComboBox( this, "SoundQuality", 6, false );
  41. m_pSoundQualityCombo->AddItem( "#GameUI_High", new KeyValues("SoundQuality", "quality", SOUNDQUALITY_HIGH) );
  42. m_pSoundQualityCombo->AddItem( "#GameUI_Medium", new KeyValues("SoundQuality", "quality", SOUNDQUALITY_MEDIUM) );
  43. m_pSoundQualityCombo->AddItem( "#GameUI_Low", new KeyValues("SoundQuality", "quality", SOUNDQUALITY_LOW) );
  44. m_pSpeakerSetupCombo = new ComboBox( this, "SpeakerSetup", 6, false );
  45. #ifndef POSIX
  46. m_pSpeakerSetupCombo->AddItem( "#GameUI_Headphones", new KeyValues("SpeakerSetup", "speakers", 0) );
  47. #endif
  48. m_pSpeakerSetupCombo->AddItem( "#GameUI_2Speakers", new KeyValues("SpeakerSetup", "speakers", 2) );
  49. #ifndef POSIX
  50. m_pSpeakerSetupCombo->AddItem( "#GameUI_4Speakers", new KeyValues("SpeakerSetup", "speakers", 4) );
  51. m_pSpeakerSetupCombo->AddItem( "#GameUI_5Speakers", new KeyValues("SpeakerSetup", "speakers", 5) );
  52. m_pSpeakerSetupCombo->AddItem( "#GameUI_7Speakers", new KeyValues("SpeakerSetup", "speakers", 7) );
  53. #endif
  54. m_pSpokenLanguageCombo = new ComboBox (this, "AudioSpokenLanguage", 6, false );
  55. m_pSoundMuteLoseFocusCheckButton = new CCvarToggleCheckButton( this, "snd_mute_losefocus", "#GameUI_SndMuteLoseFocus", "snd_mute_losefocus" );
  56. LoadControlSettings("Resource\\OptionsSubAudio.res");
  57. }
  58. //-----------------------------------------------------------------------------
  59. // Purpose: Destructor
  60. //-----------------------------------------------------------------------------
  61. COptionsSubAudio::~COptionsSubAudio()
  62. {
  63. }
  64. //-----------------------------------------------------------------------------
  65. // Purpose: Reloads data
  66. //-----------------------------------------------------------------------------
  67. void COptionsSubAudio::OnResetData()
  68. {
  69. m_bRequireRestart = false;
  70. m_pSFXSlider->Reset();
  71. m_pMusicSlider->Reset();
  72. // reset the combo boxes
  73. // close captions
  74. ConVarRef closecaption("closecaption");
  75. ConVarRef cc_subtitles("cc_subtitles");
  76. if (closecaption.GetBool())
  77. {
  78. if (cc_subtitles.GetBool())
  79. {
  80. m_pCloseCaptionCombo->ActivateItem(2);
  81. }
  82. else
  83. {
  84. m_pCloseCaptionCombo->ActivateItem(1);
  85. }
  86. }
  87. else
  88. {
  89. m_pCloseCaptionCombo->ActivateItem(0);
  90. }
  91. // speakers
  92. ConVarRef snd_surround_speakers("Snd_Surround_Speakers");
  93. int speakers = snd_surround_speakers.GetInt();
  94. #ifdef POSIX
  95. // On Posix there is no headphone option, so we upgrade to 2 speakers if Snd_Surround_Speakers == 0
  96. if ( speakers == 0 )
  97. speakers = 2;
  98. #endif
  99. // if Snd_Surround_Speakers is -1, then upgrade to 2 speakers
  100. if ( speakers < 0 )
  101. speakers = 2;
  102. {for (int itemID = 0; itemID < m_pSpeakerSetupCombo->GetItemCount(); itemID++)
  103. {
  104. KeyValues *kv = m_pSpeakerSetupCombo->GetItemUserData( itemID );
  105. if (kv && kv->GetInt( "speakers" ) == speakers)
  106. {
  107. m_pSpeakerSetupCombo->ActivateItem( itemID );
  108. break;
  109. }
  110. }}
  111. // sound quality is made up from several cvars
  112. ConVarRef Snd_PitchQuality("Snd_PitchQuality");
  113. ConVarRef dsp_slow_cpu("dsp_slow_cpu");
  114. int quality = SOUNDQUALITY_LOW;
  115. if (dsp_slow_cpu.GetBool() == false)
  116. {
  117. quality = SOUNDQUALITY_MEDIUM;
  118. }
  119. if (Snd_PitchQuality.GetBool())
  120. {
  121. quality = SOUNDQUALITY_HIGH;
  122. }
  123. // find the item in the list and activate it
  124. {for (int itemID = 0; itemID < m_pSoundQualityCombo->GetItemCount(); itemID++)
  125. {
  126. KeyValues *kv = m_pSoundQualityCombo->GetItemUserData(itemID);
  127. if (kv && kv->GetInt("quality") == quality)
  128. {
  129. m_pSoundQualityCombo->ActivateItem(itemID);
  130. }
  131. }}
  132. //
  133. // Audio Languages
  134. //
  135. char szCurrentLanguage[50];
  136. char szAvailableLanguages[512];
  137. szCurrentLanguage[0] = 0;
  138. szAvailableLanguages[0] = 0;
  139. // Fallback to current engine language
  140. engine->GetUILanguage( szCurrentLanguage, sizeof( szCurrentLanguage ));
  141. // In a Steam environment we get the current language
  142. #if !defined( NO_STEAM )
  143. // When Steam isn't running we can't get the language info...
  144. if ( steamapicontext->SteamApps() )
  145. {
  146. Q_strncpy( szCurrentLanguage, steamapicontext->SteamApps()->GetCurrentGameLanguage(), sizeof(szCurrentLanguage) );
  147. Q_strncpy( szAvailableLanguages, steamapicontext->SteamApps()->GetAvailableGameLanguages(), sizeof(szAvailableLanguages) );
  148. }
  149. #endif
  150. // Get the spoken language and store it for comparison purposes
  151. m_nCurrentAudioLanguage = PchLanguageToELanguage( szCurrentLanguage );
  152. // Check to see if we have a list of languages from Steam
  153. if ( V_strlen( szAvailableLanguages ) )
  154. {
  155. // Populate the combo box with each available language
  156. CUtlVector<char*> languagesList;
  157. V_SplitString( szAvailableLanguages, ",", languagesList );
  158. for ( int i=0; i < languagesList.Count(); i++ )
  159. {
  160. const ELanguage languageCode = PchLanguageToELanguage( languagesList[i] );
  161. m_pSpokenLanguageCombo->AddItem( GetLanguageVGUILocalization( languageCode ), new KeyValues ("Audio Languages", "language", languageCode) );
  162. }
  163. languagesList.PurgeAndDeleteElements();
  164. }
  165. else
  166. {
  167. // Add the current language to the combo
  168. m_pSpokenLanguageCombo->AddItem( GetLanguageVGUILocalization( m_nCurrentAudioLanguage ), new KeyValues ("Audio Languages", "language", m_nCurrentAudioLanguage) );
  169. }
  170. // Activate the current language in the combo
  171. {for (int itemID = 0; itemID < m_pSpokenLanguageCombo->GetItemCount(); itemID++)
  172. {
  173. KeyValues *kv = m_pSpokenLanguageCombo->GetItemUserData( itemID );
  174. if ( kv && kv->GetInt( "language" ) == m_nCurrentAudioLanguage )
  175. {
  176. m_pSpokenLanguageCombo->ActivateItem( itemID );
  177. break;
  178. }
  179. }}
  180. m_pSoundMuteLoseFocusCheckButton->Reset();
  181. }
  182. //-----------------------------------------------------------------------------
  183. // Purpose: Applies changes
  184. //-----------------------------------------------------------------------------
  185. void COptionsSubAudio::OnApplyChanges()
  186. {
  187. m_pSFXSlider->ApplyChanges();
  188. m_pMusicSlider->ApplyChanges();
  189. // set the cvars appropriately
  190. // Tracker 28933: Note we can't do this because closecaption is marked
  191. // FCVAR_USERINFO and it won't get sent to server is we direct set it, we
  192. // need to pass it along to the engine parser!!!
  193. // ConVar *closecaption = (ConVar *)cvar->FindVar("closecaption");
  194. int closecaption_value = 0;
  195. ConVarRef cc_subtitles( "cc_subtitles" );
  196. switch (m_pCloseCaptionCombo->GetActiveItem())
  197. {
  198. default:
  199. case 0:
  200. closecaption_value = 0;
  201. cc_subtitles.SetValue( 0 );
  202. break;
  203. case 1:
  204. closecaption_value = 1;
  205. cc_subtitles.SetValue( 0 );
  206. break;
  207. case 2:
  208. closecaption_value = 1;
  209. cc_subtitles.SetValue( 1 );
  210. break;
  211. }
  212. // Stuff the close caption change to the console so that it can be
  213. // sent to the server (FCVAR_USERINFO) so that you don't have to restart
  214. // the level for the change to take effect.
  215. char cmd[ 64 ];
  216. Q_snprintf( cmd, sizeof( cmd ), "closecaption %i\n", closecaption_value );
  217. engine->ClientCmd_Unrestricted( cmd );
  218. ConVarRef snd_surround_speakers( "Snd_Surround_Speakers" );
  219. int speakers = m_pSpeakerSetupCombo->GetActiveItemUserData()->GetInt( "speakers" );
  220. snd_surround_speakers.SetValue( speakers );
  221. // quality
  222. ConVarRef Snd_PitchQuality( "Snd_PitchQuality" );
  223. ConVarRef dsp_slow_cpu( "dsp_slow_cpu" );
  224. int quality = m_pSoundQualityCombo->GetActiveItemUserData()->GetInt( "quality" );
  225. switch ( quality )
  226. {
  227. case SOUNDQUALITY_LOW:
  228. dsp_slow_cpu.SetValue(true);
  229. Snd_PitchQuality.SetValue(false);
  230. break;
  231. case SOUNDQUALITY_MEDIUM:
  232. dsp_slow_cpu.SetValue(false);
  233. Snd_PitchQuality.SetValue(false);
  234. break;
  235. default:
  236. Assert("Undefined sound quality setting.");
  237. case SOUNDQUALITY_HIGH:
  238. dsp_slow_cpu.SetValue(false);
  239. Snd_PitchQuality.SetValue(true);
  240. break;
  241. };
  242. // headphones at high quality get enhanced stereo turned on
  243. ConVarRef dsp_enhance_stereo( "dsp_enhance_stereo" );
  244. if (speakers == 0 && quality == SOUNDQUALITY_HIGH)
  245. {
  246. dsp_enhance_stereo.SetValue( 1 );
  247. }
  248. else
  249. {
  250. dsp_enhance_stereo.SetValue( 0 );
  251. }
  252. // Audio spoken language
  253. KeyValues *kv = m_pSpokenLanguageCombo->GetItemUserData( m_pSpokenLanguageCombo->GetActiveItem() );
  254. const ELanguage nUpdatedAudioLanguage = (ELanguage)( kv ? kv->GetInt( "language" ) : k_Lang_English );
  255. if ( nUpdatedAudioLanguage != m_nCurrentAudioLanguage )
  256. {
  257. // Store new language in static member so that it can be accessed during shutdown when this instance is gone
  258. m_pchUpdatedAudioLanguage = (char *) GetLanguageShortName( nUpdatedAudioLanguage );
  259. // Inform user that they need to restart in order change language at this time
  260. QueryBox *qb = new QueryBox( "#GameUI_ChangeLanguageRestart_Title", "#GameUI_ChangeLanguageRestart_Info", GetParent()->GetParent()->GetParent() );
  261. if (qb != NULL)
  262. {
  263. qb->SetOKCommand( new KeyValues( "Command", "command", "RestartWithNewLanguage" ) );
  264. qb->SetOKButtonText( "#GameUI_ChangeLanguageRestart_OkButton" );
  265. qb->SetCancelButtonText( "#GameUI_ChangeLanguageRestart_CancelButton" );
  266. qb->AddActionSignalTarget( GetParent()->GetParent()->GetParent() );
  267. qb->DoModal();
  268. }
  269. }
  270. m_pSoundMuteLoseFocusCheckButton->ApplyChanges();
  271. }
  272. //-----------------------------------------------------------------------------
  273. // Purpose: Called on controls changing, enables the Apply button
  274. //-----------------------------------------------------------------------------
  275. void COptionsSubAudio::OnControlModified()
  276. {
  277. PostActionSignal(new KeyValues("ApplyButtonEnable"));
  278. }
  279. //-----------------------------------------------------------------------------
  280. // Purpose: returns true if the engine needs to be restarted
  281. //-----------------------------------------------------------------------------
  282. bool COptionsSubAudio::RequiresRestart()
  283. {
  284. // nothing in audio requires a restart like now
  285. return false;
  286. }
  287. //-----------------------------------------------------------------------------
  288. // Purpose:
  289. //-----------------------------------------------------------------------------
  290. void COptionsSubAudio::OnCommand( const char *command )
  291. {
  292. if ( !stricmp( command, "TestSpeakers" ) )
  293. {
  294. // ask them if they REALLY want to test the speakers if they're in a game already.
  295. if (engine->IsConnected())
  296. {
  297. QueryBox *qb = new QueryBox("#GameUI_TestSpeakersWarning_Title", "#GameUI_TestSpeakersWarning_Info" );
  298. if (qb != NULL)
  299. {
  300. qb->SetOKCommand(new KeyValues("RunTestSpeakers"));
  301. qb->SetOKButtonText("#GameUI_TestSpeakersWarning_OkButton");
  302. qb->SetCancelButtonText("#GameUI_TestSpeakersWarning_CancelButton");
  303. qb->AddActionSignalTarget( this );
  304. qb->DoModal();
  305. }
  306. else
  307. {
  308. // couldn't create the warning dialog for some reason, so just test the speakers.
  309. RunTestSpeakers();
  310. }
  311. }
  312. else
  313. {
  314. // player isn't connected to a game so there's no reason to warn them about being disconnected.
  315. // create the command to execute
  316. RunTestSpeakers();
  317. }
  318. }
  319. else if ( !stricmp( command, "ShowThirdPartyAudioCredits" ) )
  320. {
  321. OpenThirdPartySoundCreditsDialog();
  322. }
  323. BaseClass::OnCommand( command );
  324. }
  325. //-----------------------------------------------------------------------------
  326. // Purpose: Run the test speakers map.
  327. //-----------------------------------------------------------------------------
  328. void COptionsSubAudio::RunTestSpeakers()
  329. {
  330. engine->ClientCmd_Unrestricted( "disconnect\nwait\nwait\nsv_lan 1\nsetmaster enable\nmaxplayers 1\n\nhostname \"Speaker Test\"\nprogress_enable\nmap test_speakers\n" );
  331. }
  332. //-----------------------------------------------------------------------------
  333. // Purpose: third-party audio credits dialog
  334. //-----------------------------------------------------------------------------
  335. class COptionsSubAudioThirdPartyCreditsDlg : public vgui::Frame
  336. {
  337. DECLARE_CLASS_SIMPLE( COptionsSubAudioThirdPartyCreditsDlg, vgui::Frame );
  338. public:
  339. COptionsSubAudioThirdPartyCreditsDlg( vgui::VPANEL hParent ) : BaseClass( NULL, NULL )
  340. {
  341. // parent is ignored, since we want look like we're steal focus from the parent (we'll become modal below)
  342. SetTitle("#GameUI_ThirdPartyAudio_Title", true);
  343. SetSize( 500, 200 );
  344. LoadControlSettings( "resource/OptionsSubAudioThirdPartyDlg.res" );
  345. MoveToCenterOfScreen();
  346. SetSizeable( false );
  347. SetDeleteSelfOnClose( true );
  348. }
  349. virtual void Activate()
  350. {
  351. BaseClass::Activate();
  352. input()->SetAppModalSurface(GetVPanel());
  353. }
  354. void OnKeyCodeTyped(KeyCode code)
  355. {
  356. // force ourselves to be closed if the escape key it pressed
  357. if (code == KEY_ESCAPE)
  358. {
  359. Close();
  360. }
  361. else
  362. {
  363. BaseClass::OnKeyCodeTyped(code);
  364. }
  365. }
  366. };
  367. //-----------------------------------------------------------------------------
  368. // Purpose: Open third party audio credits dialog
  369. //-----------------------------------------------------------------------------
  370. void COptionsSubAudio::OpenThirdPartySoundCreditsDialog()
  371. {
  372. if (!m_OptionsSubAudioThirdPartyCreditsDlg.Get())
  373. {
  374. m_OptionsSubAudioThirdPartyCreditsDlg = new COptionsSubAudioThirdPartyCreditsDlg(GetVParent());
  375. }
  376. m_OptionsSubAudioThirdPartyCreditsDlg->Activate();
  377. }