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.

408 lines
9.5 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. // voice_tweakDlg.cpp : implementation file
  9. //
  10. #include "stdafx.h"
  11. #include "voice_tweak.h"
  12. #include "voice_tweakDlg.h"
  13. #include "voice_gain.h"
  14. #include "dvoice.h"
  15. void TermDPlayVoice( HINSTANCE &hInst, IDirectPlayVoiceTest* &pVoice )
  16. {
  17. if( pVoice )
  18. {
  19. pVoice->Release();
  20. pVoice = NULL;
  21. }
  22. if( hInst )
  23. {
  24. FreeLibrary( hInst );
  25. hInst = NULL;
  26. }
  27. }
  28. bool InitDPlayVoice( HINSTANCE &hInst, IDirectPlayVoiceTest* &pVoice )
  29. {
  30. typedef HRESULT (WINAPI *DirectPlayVoiceCreateFn)(
  31. GUID* pcIID,
  32. void** ppvInterface,
  33. IUnknown* pUnknown
  34. );
  35. hInst = NULL;
  36. pVoice = NULL;
  37. hInst = LoadLibrary( "dpvoice.dll" );
  38. if(hInst)
  39. {
  40. DirectPlayVoiceCreateFn fn = (DirectPlayVoiceCreateFn)GetProcAddress(hInst, "DirectPlayVoiceCreate");
  41. if(fn)
  42. {
  43. HRESULT hr = fn((GUID*)&IID_IDirectPlayVoiceTest, (void**)&pVoice, NULL);
  44. if( SUCCEEDED( hr ) )
  45. return true;
  46. }
  47. }
  48. TermDPlayVoice( hInst, pVoice );
  49. return false;
  50. }
  51. bool IsDPlayVoiceAvailable()
  52. {
  53. HINSTANCE hInst;
  54. IDirectPlayVoiceTest *pVoice;
  55. bool bRet = InitDPlayVoice( hInst, pVoice );
  56. TermDPlayVoice( hInst, pVoice );
  57. return bRet;
  58. }
  59. #ifdef _DEBUG
  60. #define new DEBUG_NEW
  61. #undef THIS_FILE
  62. static char THIS_FILE[] = __FILE__;
  63. #endif
  64. #define VOLUMESLIDER_RANGE 1000
  65. CVoiceTweakApp* TweakApp() {return (CVoiceTweakApp*)AfxGetApp();}
  66. extern "C"
  67. {
  68. void Con_DPrintf (char *fmt, ...);
  69. void Con_Printf (char *fmt, ...);
  70. }
  71. void PrintToTraceWindow(const char *fmt, va_list marker)
  72. {
  73. char msg[2048];
  74. _vsnprintf(msg, sizeof(msg), fmt, marker);
  75. OutputDebugString(msg);
  76. }
  77. void Con_DPrintf (char *fmt, ...)
  78. {
  79. va_list marker;
  80. va_start(marker, fmt);
  81. PrintToTraceWindow(fmt, marker);
  82. va_end(marker);
  83. }
  84. void Con_Printf (char *fmt, ...)
  85. {
  86. va_list marker;
  87. va_start(marker, fmt);
  88. PrintToTraceWindow(fmt, marker);
  89. va_end(marker);
  90. }
  91. /////////////////////////////////////////////////////////////////////////////
  92. // CVoiceTweakDlg dialog
  93. CVoiceTweakDlg::CVoiceTweakDlg(CWnd* pParent /*=NULL*/)
  94. : CDialog(CVoiceTweakDlg::IDD, pParent)
  95. {
  96. //{{AFX_DATA_INIT(CVoiceTweakDlg)
  97. //}}AFX_DATA_INIT
  98. // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
  99. m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
  100. }
  101. CVoiceTweakDlg::~CVoiceTweakDlg()
  102. {
  103. Term();
  104. }
  105. void CVoiceTweakDlg::Term()
  106. {
  107. m_WinIdle.EndIdle();
  108. }
  109. void CVoiceTweakDlg::DoDataExchange(CDataExchange* pDX)
  110. {
  111. CDialog::DoDataExchange(pDX);
  112. //{{AFX_DATA_MAP(CVoiceTweakDlg)
  113. DDX_Control(pDX, IDC_HARDWAREGAIN, m_HardwareGain);
  114. DDX_Control(pDX, IDC_VOICEVOLUME, m_VoiceVolume);
  115. DDX_Control(pDX, IDC_VOLUMESLIDER, m_VolumeSlider);
  116. DDX_Control(pDX, IDC_INSTRUCTIONTEXT, m_InstructionText);
  117. //}}AFX_DATA_MAP
  118. }
  119. BEGIN_MESSAGE_MAP(CVoiceTweakDlg, CDialog)
  120. //{{AFX_MSG_MAP(CVoiceTweakDlg)
  121. ON_WM_PAINT()
  122. ON_WM_QUERYDRAGICON()
  123. ON_MESSAGE(WM_TWEAKIDLE, OnIdle)
  124. ON_WM_DESTROY()
  125. ON_BN_CLICKED(IDC_HARDWAREGAIN, OnHardwareGain)
  126. ON_BN_CLICKED(IDFURTHERHELP, OnFurtherhelp)
  127. ON_BN_CLICKED(IDSYSTEMSETUP, OnSystemSetup)
  128. //}}AFX_MSG_MAP
  129. END_MESSAGE_MAP()
  130. IMixerControls* GetAppMixerControls()
  131. {
  132. return ((CVoiceTweakApp*)AfxGetApp())->m_pMixerControls;
  133. }
  134. void CVoiceTweakDlg::SetString(int childControl, int stringID)
  135. {
  136. if(CWnd *pWnd = GetDlgItem(childControl))
  137. {
  138. CString str;
  139. str.LoadString(stringID);
  140. pWnd->SetWindowText(str);
  141. }
  142. }
  143. /////////////////////////////////////////////////////////////////////////////
  144. // CVoiceTweakDlg message handlers
  145. BOOL CVoiceTweakDlg::OnInitDialog()
  146. {
  147. CDialog::OnInitDialog();
  148. // Set the icon for this dialog. The framework does this automatically
  149. // when the application's main window is not a dialog
  150. SetIcon(m_hIcon, TRUE); // Set big icon
  151. SetIcon(m_hIcon, FALSE); // Set small icon
  152. CString str;
  153. str.LoadString( MapLanguageStringID(IDS_HELPTEXT) );
  154. m_InstructionText.SetWindowText(str);
  155. // Save off their old settings so we can restore if they hit cancel.
  156. GetAppMixerControls()->GetValue_Float(IMixerControls::Control::MicVolume, m_OldVolume);
  157. float bBoostOn;
  158. if(GetAppMixerControls()->GetValue_Float(IMixerControls::Control::MicBoost, bBoostOn))
  159. {
  160. m_HardwareGain.SetCheck((int)bBoostOn);
  161. }
  162. else
  163. {
  164. m_HardwareGain.ShowWindow(SW_HIDE);
  165. }
  166. // Initialize the volume control.
  167. m_VolumeSlider.SetRange(0, 1000);
  168. m_VolumeSlider.SetPos((int)(VOLUMESLIDER_RANGE - m_OldVolume * VOLUMESLIDER_RANGE));
  169. m_VoiceVolume.SetRange32(0, (1 << (BYTES_PER_SAMPLE*8-1)) - 1);
  170. // Get idle messages...
  171. m_WinIdle.StartIdle(GetSafeHwnd(), WM_TWEAKIDLE, 0,0, 10);
  172. m_WinIdle.NextIdle();
  173. // Set all the dialog item strings for localization.
  174. SetString(IDOK, MapLanguageStringID(IDS_OKAY));
  175. SetString(IDCANCEL, MapLanguageStringID(IDS_CANCEL));
  176. SetString(IDC_VOICEINPUTFRAME, MapLanguageStringID(IDS_VOICEINPUT));
  177. SetString(IDC_VOLUMELABEL, MapLanguageStringID(IDS_VOLUME));
  178. SetString(IDC_HARDWAREGAIN, MapLanguageStringID(IDS_ENABLEGAIN));
  179. SetString(IDSYSTEMSETUP, MapLanguageStringID(IDS_SYSTEMSETUP));
  180. SetString(IDFURTHERHELP, MapLanguageStringID(IDS_HELP));
  181. CString titleStr;
  182. titleStr.LoadString( MapLanguageStringID(IDS_WINDOWTITLE) );
  183. SetWindowText(titleStr);
  184. if( !IsDPlayVoiceAvailable() )
  185. {
  186. CWnd *pWnd = GetDlgItem( IDSYSTEMSETUP );
  187. if( pWnd )
  188. pWnd->EnableWindow( false );
  189. }
  190. return TRUE; // return TRUE unless you set the focus to a control
  191. }
  192. // If you add a minimize button to your dialog, you will need the code below
  193. // to draw the icon. For MFC applications using the document/view model,
  194. // this is automatically done for you by the framework.
  195. void CVoiceTweakDlg::OnPaint()
  196. {
  197. if (IsIconic())
  198. {
  199. CPaintDC dc(this); // device context for painting
  200. SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
  201. // Center icon in client rectangle
  202. int cxIcon = GetSystemMetrics(SM_CXICON);
  203. int cyIcon = GetSystemMetrics(SM_CYICON);
  204. CRect rect;
  205. GetClientRect(&rect);
  206. int x = (rect.Width() - cxIcon + 1) / 2;
  207. int y = (rect.Height() - cyIcon + 1) / 2;
  208. // Draw the icon
  209. dc.DrawIcon(x, y, m_hIcon);
  210. }
  211. else
  212. {
  213. CDialog::OnPaint();
  214. }
  215. }
  216. // The system calls this to obtain the cursor to display while the user drags
  217. // the minimized window.
  218. HCURSOR CVoiceTweakDlg::OnQueryDragIcon()
  219. {
  220. return (HCURSOR) m_hIcon;
  221. }
  222. BOOL CVoiceTweakDlg::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
  223. {
  224. NMHDR *pHdr = (NMHDR*)lParam;
  225. if(pHdr->hwndFrom == m_VolumeSlider.m_hWnd)
  226. {
  227. GetAppMixerControls()->SetValue_Float(IMixerControls::Control::MicVolume, (VOLUMESLIDER_RANGE - m_VolumeSlider.GetPos()) / (float)VOLUMESLIDER_RANGE);
  228. }
  229. return CDialog::OnNotify(wParam, lParam, pResult);
  230. }
  231. LONG CVoiceTweakDlg::OnIdle(UINT a, LONG b)
  232. {
  233. static DWORD startTime = GetTickCount();
  234. if( TweakApp()->m_pVoiceRecord )
  235. {
  236. short samples[22 * 1024];
  237. // If the output has too many buffered samples, skip some data.
  238. TweakApp()->m_pWaveOut->Idle();
  239. int nBufferedSamples = TweakApp()->m_pWaveOut->GetNumBufferedSamples();
  240. float nSeconds = nBufferedSamples / (float)VOICE_TWEAK_SAMPLE_RATE;
  241. int nMinSamples = VOICE_TWEAK_SAMPLE_RATE / 5;
  242. if( nBufferedSamples < nMinSamples )
  243. {
  244. // We want at least a certain amount of buffered data.
  245. int nSamplesToAdd = nMinSamples - nBufferedSamples;
  246. memset( samples, 0, nSamplesToAdd*2 );
  247. TweakApp()->m_pWaveOut->PutSamples( samples, nSamplesToAdd );
  248. }
  249. else
  250. {
  251. // Get the samples.
  252. int nSamples = TweakApp()->m_pVoiceRecord->GetRecordedData(samples, sizeof(samples)/BYTES_PER_SAMPLE);
  253. if( nSeconds < 0.5f )
  254. {
  255. // Find the highest value.
  256. int highValue = -100000;
  257. for(int i=0; i < nSamples; i++)
  258. highValue = max(abs(samples[i]), highValue);
  259. // Set our status bar accordingly.
  260. highValue = (highValue >> 9) << 9; // Get rid of flicker.
  261. m_VoiceVolume.SetPos(highValue);
  262. // Give the samples to the wave output...
  263. if(TweakApp()->m_pWaveOut)
  264. {
  265. // Ignore the first second or so.. it's usually garbage.
  266. DWORD curTime = GetTickCount();
  267. static DWORD silentTime = 500;
  268. static DWORD fadeInTime = 1000;
  269. if( curTime - startTime < silentTime )
  270. {
  271. memset( samples, 0, nSamples*2 );
  272. }
  273. else if( (curTime-silentTime) - startTime < fadeInTime )
  274. {
  275. float flFade = ((curTime-silentTime) - startTime) / (float)fadeInTime;
  276. flFade = flFade*flFade;
  277. for( int i=0; i < nSamples; i++ )
  278. samples[i] = (short)( samples[i] * flFade );
  279. }
  280. TweakApp()->m_pWaveOut->PutSamples(samples, nSamples);
  281. }
  282. }
  283. }
  284. }
  285. // Tell the idle thread we're ready for another idle message.
  286. m_WinIdle.NextIdle();
  287. return 0;
  288. }
  289. void CVoiceTweakDlg::OnDestroy()
  290. {
  291. Term();
  292. CDialog::OnDestroy();
  293. }
  294. void CVoiceTweakDlg::OnHardwareGain()
  295. {
  296. if(m_HardwareGain.GetCheck())
  297. GetAppMixerControls()->SetValue_Float(IMixerControls::Control::MicBoost, true);
  298. else
  299. GetAppMixerControls()->SetValue_Float(IMixerControls::Control::MicBoost, false);
  300. }
  301. void CVoiceTweakDlg::OnCancel()
  302. {
  303. // Restore old settings.
  304. GetAppMixerControls()->SetValue_Float(IMixerControls::Control::MicVolume, m_OldVolume);
  305. CDialog::OnCancel();
  306. }
  307. void CVoiceTweakDlg::OnFurtherhelp()
  308. {
  309. }
  310. void CVoiceTweakDlg::OnSystemSetup()
  311. {
  312. TweakApp()->StopDevices();
  313. bool bSucceeded = false;
  314. HINSTANCE hInst;
  315. IDirectPlayVoiceTest *pVoice;
  316. if( InitDPlayVoice( hInst, pVoice ) )
  317. {
  318. pVoice->CheckAudioSetup(NULL, NULL, m_hWnd, DVFLAGS_ALLOWBACK);
  319. TermDPlayVoice( hInst, pVoice );
  320. }
  321. else
  322. {
  323. CString str;
  324. str.LoadString( MapLanguageStringID(IDS_NODPLAYVOICE) );
  325. MessageBox(str);
  326. }
  327. if(!TweakApp()->StartDevices())
  328. AfxPostQuitMessage(0);
  329. }