|
|
#include "stdafx.h"
#include "resource.h"
#include <sapi.h>
#include <string.h>
#include "TTSDlg.h"
#include "audiodlg.h"
#include <spddkhlp.h>
#include "helpresource.h"
#include "srdlg.h"
#include "richedit.h"
#include <SPCollec.h>
#include "SAPIINT.h"
#include "SpATL.h"
#include "SpAutoHandle.h"
#include "SpAutoMutex.h"
#include "SpAutoEvent.h"
#include "spvoice.h"
#include <richedit.h>
#include <richole.h>
#include "tom.h"
static DWORD aKeywordIds[] = { // Control ID // Help Context ID
IDC_COMBO_VOICES, IDH_LIST_TTS, IDC_TTS_ADV, IDH_TTS_ADV, IDC_OUTPUT_SETTINGS, IDH_OUTPUT_SETTINGS, IDC_SLIDER_SPEED, IDH_SLIDER_SPEED, IDC_EDIT_SPEAK, IDH_EDIT_SPEAK, IDC_SPEAK, IDH_SPEAK, IDC_TTS_ICON, IDH_NOHELP, IDC_DIRECTIONS, IDH_NOHELP, IDC_TTS_CAP, IDH_NOHELP, IDC_SLOW, IDH_NOHELP, IDC_NORMAL, IDH_NOHELP, IDC_FAST, IDH_NOHELP, IDC_GROUP_VOICESPEED, IDH_NOHELP, IDC_GROUP_PREVIEWVOICE, IDH_NOHELP, 0, 0 };
// Address of the TrackBar's WNDPROC
WNDPROC g_TrackBarWindowProc;
// Our own internal TrackBar WNDPROC used to intercept and process VK_UP and VK_DOWN messages
LRESULT CALLBACK MyTrackBarWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
/*****************************************************************************
* TTSDlgProc * *------------* * Description: * DLGPROC for the TTS ****************************************************************** MIKEAR ***/ INT_PTR CALLBACK TTSDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { SPDBG_FUNC( "TTSDlgProc" );
USES_CONVERSION;
switch (uMsg) { case WM_INITDIALOG: { g_pTTSDlg->OnInitDialog(hWnd); break; }
case WM_DESTROY: { g_pTTSDlg->OnDestroy(); break; } // Handle the context sensitive help
case WM_CONTEXTMENU: { WinHelp((HWND) wParam, CPL_HELPFILE, HELP_CONTEXTMENU, (DWORD_PTR)(LPSTR) aKeywordIds); break; }
case WM_HELP: { WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, CPL_HELPFILE, HELP_WM_HELP,(DWORD_PTR)(LPSTR) aKeywordIds); break; }
case WM_HSCROLL: { g_pTTSDlg->ChangeSpeed();
break; }
case WM_NOTIFY: switch (((NMHDR*)lParam)->code) { case PSN_APPLY: { g_pTTSDlg->OnApply(); break; } case PSN_KILLACTIVE: { // if the voice is speaking, stop it before switching tabs
if (g_pTTSDlg->m_bIsSpeaking) { g_pTTSDlg->Speak(); }
break; }
case PSN_QUERYCANCEL: // user clicks the Cancel button
{ if ( g_pSRDlg ) { g_pSRDlg->OnCancel(); } break; } } break;
case WM_COMMAND: switch ( LOWORD(wParam) ) { case IDC_COMBO_VOICES: { if ( CBN_SELCHANGE == HIWORD(wParam) ) { HRESULT hr = g_pTTSDlg->DefaultVoiceChange(false); if ( SUCCEEDED( hr ) ) { g_pTTSDlg->Speak(); } } break; } case IDC_OUTPUT_SETTINGS: { // if it's speaking make it stop
g_pTTSDlg->StopSpeak();
::SetFocus(GetDlgItem(g_pTTSDlg->m_hDlg, IDC_OUTPUT_SETTINGS));
// The m_pAudioDlg will be non-NULL only if the audio dialog
// has been previously brough up.
// Otherwise, we need a newly-initialized one
if ( !g_pTTSDlg->m_pAudioDlg ) { g_pTTSDlg->m_pAudioDlg = new CAudioDlg(eOUTPUT ); } if (g_pTTSDlg->m_pAudioDlg != NULL) { ::DialogBoxParam( _Module.GetResourceInstance(), MAKEINTRESOURCE( IDD_AUDIO_DEFAULT ), hWnd, AudioDlgProc, (LPARAM) g_pTTSDlg->m_pAudioDlg );
if ( g_pTTSDlg->m_pAudioDlg->IsAudioDeviceChangedSinceLastTime() ) { // Warn the user that he needs to apply the changes
WCHAR szWarning[MAX_LOADSTRING]; szWarning[0] = 0; LoadString( _Module.GetResourceInstance(), IDS_AUDIOOUT_CHANGE_WARNING, szWarning, MAX_LOADSTRING); MessageBox( g_pTTSDlg->GetHDlg(), szWarning, g_pTTSDlg->m_szCaption, MB_ICONWARNING | g_dwIsRTLLayout ); } }
g_pTTSDlg->KickCPLUI();
break; }
case IDC_EDIT_SPEAK: { if (HIWORD(wParam) == EN_CHANGE) // user is changing text
{ g_pTTSDlg->SetEditModified(true); }
break; }
case IDC_SPEAK: { g_pTTSDlg->Speak(); break; }
case IDC_TTS_ADV: { // convert the title of the window to wide chars
CSpDynamicString dstrTitle; WCHAR szTitle[256]; szTitle[0] = '\0'; LoadString(_Module.GetResourceInstance(), IDS_ENGINE_SETTINGS, szTitle, sp_countof(szTitle)); dstrTitle = szTitle; HRESULT hr = g_pTTSDlg->m_cpCurVoiceToken->DisplayUI( hWnd, dstrTitle, SPDUI_EngineProperties, NULL, 0, NULL ); if ( FAILED( hr ) ) { WCHAR szError[ MAX_LOADSTRING ]; ::LoadString( _Module.GetResourceInstance(), IDS_TTSUI_ERROR, szError, sp_countof( szError ) ); ::MessageBox( hWnd, szError, g_pTTSDlg->m_szCaption, MB_ICONEXCLAMATION | g_dwIsRTLLayout ); ::EnableWindow( ::GetDlgItem( hWnd, IDC_TTS_ADV ), FALSE ); } break; } } break; }
return FALSE; } /* TTSDlgProc */
/*****************************************************************************
* MyTrackBarWindowProc * *------------* * Description: * This is our own privately sub-classed WNDPROC for the rate TrackBar. We * tell the TTS dialog to use this one so we can pre-process the VK_UP and * VK_DOWN messages before the TrackBar's WNDPROC "incorrectly" handles them * on it's own. All other messages we just pass through to the TrackBar's * WNDPROC. ****************************************************************** Leonro ***/ LRESULT CALLBACK MyTrackBarWindowProc( HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
) { switch( uMsg ) { case WM_KEYDOWN: case WM_KEYUP: if( wParam == VK_UP ) { wParam = VK_RIGHT; } else if( wParam == VK_DOWN ) { wParam = VK_LEFT; } break; }
return CallWindowProc( g_TrackBarWindowProc, hwnd, uMsg, wParam, lParam ); }
/*****************************************************************************
* CTTSDlg::SetEditModified( bool fModify ) * *-----------------------* * Description: * Access method for m_fTextModified ****************************************************************** BRENTMID ***/ void CTTSDlg::SetEditModified( bool fModify ) { m_fTextModified = fModify; }
/*****************************************************************************
* CTTSDlg::OnInitDialog * *-----------------------* * Description: * Dialog Initialization ****************************************************************** BECKYW ***/ void CTTSDlg::OnInitDialog(HWND hWnd) { USES_CONVERSION; SPDBG_FUNC( "CTTSDlg::OnInitDialog" ); SPDBG_ASSERT(IsWindow(hWnd)); m_hDlg = hWnd;
// Put text on the speak button
ChangeSpeakButton();
// This is to be the caption for error messages
m_szCaption[0] = 0; ::LoadString( _Module.GetResourceInstance(), IDS_CAPTION, m_szCaption, sp_countof( m_szCaption ) );
// Initialize the TTS personality list
InitTTSList( hWnd );
// Set the range on the slider
HWND hSlider = ::GetDlgItem( hWnd, IDC_SLIDER_SPEED ); ::SendMessage( hSlider, TBM_SETRANGE, true, MAKELONG( VOICE_MIN_SPEED, VOICE_MAX_SPEED ) );
// Retrieve address of the TrackBar's WNDPROC so we can sub-class it and intercept
// and process the VK_UP and VK_DOWN messages before it handle's them on it's
// own "incorrectly"
g_TrackBarWindowProc = (WNDPROC)GetWindowLongPtr( hSlider, GWLP_WNDPROC );
// Set the WNDPROC of the TrackBar to MyTrackBarWindowProc
SetWindowLongPtr( hSlider, GWLP_WNDPROC, (LONG_PTR)MyTrackBarWindowProc );
// Limit the text in the preview pane
::SendDlgItemMessage( hWnd, IDC_EDIT_SPEAK, EM_LIMITTEXT, MAX_EDIT_TEXT - 1, 0 );
// Find the original default token
SpGetDefaultTokenFromCategoryId( SPCAT_VOICES, &m_cpOriginalDefaultVoiceToken );
// Set the appropriate voice
DefaultVoiceChange(true);
} /* CTTSDlg::OnInitDialog */
/*****************************************************************************
* CTTSDlg::InitTTSList * *----------------------* * Description: * Initializes the list control for the TTS dialog box. *******************************************************************BECKYW****/ void CTTSDlg::InitTTSList( HWND hWnd ) { m_hTTSCombo = ::GetDlgItem( hWnd, IDC_COMBO_VOICES );
SpInitTokenComboBox( m_hTTSCombo, SPCAT_VOICES ); }
/*****************************************************************************
* CTTSDlg::OnDestroy * *--------------------* * Description: * Destruction ****************************************************************** MIKEAR ***/ void CTTSDlg::OnDestroy() { SPDBG_FUNC( "CTTSDlg::OnDestroy" );
if (m_cpVoice) { m_cpVoice->SetNotifySink(NULL); m_cpVoice.Release(); }
// Let go of the tokens in the combo box
SpDestroyTokenComboBox( m_hTTSCombo );
} /* CTTSDlg::OnDestroy */
/*****************************************************************************
* CTTSDlg::OnApply * *------------------* * Description: * Set user specified options ****************************************************************** BECKYW ***/ void CTTSDlg::OnApply() { // SOFTWARE ENGINEERING OPPORTUNITY (BeckyW 7/28/00): This needs to
// return an error code
SPDBG_FUNC( "CTTSDlg::OnApply" );
m_bApplied = true;
// Store the current voice
HRESULT hr = E_FAIL; if (m_cpCurVoiceToken) { hr = SpSetDefaultTokenForCategoryId(SPCAT_VOICES, m_cpCurVoiceToken ); } if ( SUCCEEDED( hr ) ) { m_cpOriginalDefaultVoiceToken = m_cpCurVoiceToken; }
// Store the current audio out
hr = S_OK; if ( m_pAudioDlg ) { hr = m_pAudioDlg->OnApply(); if ( FAILED( hr ) ) { WCHAR szError[256]; szError[0] = '\0'; LoadString(_Module.GetResourceInstance(), IDS_AUDIO_CHANGE_FAILED, szError, sp_countof(szError)); MessageBox(m_hDlg, szError, m_szCaption, MB_ICONWARNING | g_dwIsRTLLayout); }
// Kill the audio dialog, as we are done with it.
delete m_pAudioDlg; m_pAudioDlg = NULL;
// Re-create the voice, since we have audio changes to pick up
DefaultVoiceChange(false); }
// Store the voice rate in the registry
int iCurRate = 0; HWND hSlider = ::GetDlgItem( m_hDlg, IDC_SLIDER_SPEED ); iCurRate = (int)::SendMessage( hSlider, TBM_GETPOS, 0, 0 );
CComPtr<ISpObjectTokenCategory> cpCategory; if (SUCCEEDED(SpGetCategoryFromId(SPCAT_VOICES, &cpCategory))) { CComPtr<ISpDataKey> cpDataKey; if (SUCCEEDED(cpCategory->GetDataKey(SPDKL_CurrentUser, &cpDataKey))) { cpDataKey->SetDWORD(SPVOICECATEGORY_TTSRATE, iCurRate); } }
// Keep around the slider position for determining UI state later
m_iOriginalRateSliderPos = iCurRate;
KickCPLUI();
} /* CTTSDlg::OnApply */
/*****************************************************************************
* CTTSDlg::PopulateEditCtrl * *---------------------------* * Description: * Populates the edit control with the name of the default voice. ****************************************************************** MIKEAR ***/ void CTTSDlg::PopulateEditCtrl( ISpObjectToken * pToken ) { SPDBG_FUNC( "CTTSDlg::PopulateEditCtrl" ); HRESULT hr = S_OK;
// Richedit/TOM
CComPtr<IRichEditOle> cpRichEdit; // OLE interface to the rich edit control
CComPtr<ITextDocument> cpTextDoc; CComPtr<ITextRange> cpTextRange; LANGID langId;
WCHAR text[128], editText[MAX_PATH]; HWND hWndEdit = ::GetDlgItem(m_hDlg, IDC_EDIT_SPEAK);
CSpDynamicString dstrDescription; if ((SUCCEEDED(SpGetLanguageFromVoiceToken(pToken, &langId))) && (!m_fTextModified)) { CComPtr<ISpObjectTokenCategory> cpCategory; CComPtr<ISpDataKey> cpAttributesKey; CComPtr<ISpDataKey> cpPreviewKey; CComPtr<ISpDataKey> cpVoicesKey; int len;
// First get language of voice from token.
hr = SpGetDescription(pToken, &dstrDescription, langId); // Now get hold of preview key to try and find the appropriate text.
if (SUCCEEDED(hr)) { hr = SpGetCategoryFromId(SPCAT_VOICES, &cpCategory); } if (SUCCEEDED(hr)) { hr = cpCategory->GetDataKey(SPDKL_LocalMachine, &cpVoicesKey); } if (SUCCEEDED(hr)) { hr = cpVoicesKey->OpenKey(L"Preview", &cpPreviewKey); } if (SUCCEEDED(hr)) { CSpDynamicString dstrText; swprintf(text, L"%x", langId); hr = cpPreviewKey->GetStringValue(text, &dstrText); if (SUCCEEDED(hr)) { wcsncpy(text, dstrText, 127); text[127] = 0; } } // If preview key did not contain appropriate text, fall back to the hard-coded (and
// potentially localized) text in the cpl resources.
if (FAILED(hr)) { len = LoadString( _Module.GetResourceInstance(), IDS_DEF_VOICE_TEXT, text, 128); if (len != 0) { hr = S_OK; } } if(SUCCEEDED(hr)) { WCHAR *pFirstP = wcschr(text, L'%'); WCHAR *pLastP = wcsrchr(text, L'%');
// Preview string with no %s or one %s is valid. Other kinds are not so just use description string
if(!pFirstP || (pFirstP == pLastP && ((*(pFirstP + 1) == L's' || *(pFirstP + 1) == L'S')))) { _snwprintf( editText, MAX_PATH, text, dstrDescription ); } else { _snwprintf( editText, MAX_PATH, L"%s", dstrDescription ); }
// truncate string if too long
editText[MAX_PATH - 1] = L'\0';
::SendMessage( hWndEdit, EM_GETOLEINTERFACE, 0, (LPARAM)(LPVOID FAR *)&cpRichEdit ); if ( !cpRichEdit ) { hr = E_FAIL; } if (SUCCEEDED(hr)) { hr = cpRichEdit->QueryInterface( IID_ITextDocument, (void**)&cpTextDoc ); } if (SUCCEEDED(hr)) { hr = cpTextDoc->Range(0, MAX_EDIT_TEXT-1, &cpTextRange); } if (SUCCEEDED(hr)) { BSTR bstrText = SysAllocString(editText); hr = cpTextRange->SetText(bstrText); SysFreeString(bstrText); } if (FAILED(hr)) { // Do best we can with this API - unicode languages not equal to the OS language will be replaced with ???'s.
SetWindowText(hWndEdit, editText ); } } } } /* CTTSDlg::PopulateEditCtrl */
/*****************************************************************************
* CTTSDlg::DefaultVoiceChange * *-----------------------------* * Description: * Changes the current default voice. * If there is already a default voice in effect (i.e., if this is * not the first time we are calling this function), removes the * checkmark from the appropriate item in the list. * Sets the checkmark to the appropriate item in the list. * Sets the voice with the appropriate token. * Return: * S_OK * E_UNEXPECTED if there is no token currently selected in the voice list * failure codes of SAPI voice initialization functions ******************************************************************* BECKYW ***/ HRESULT CTTSDlg::DefaultVoiceChange(bool fUsePersistentRate) { SPDBG_FUNC( "CTTSDlg::DefaultVoiceChange" ); if ( m_bIsSpeaking ) { // This forces the voice to stop speaking
m_cpVoice->Speak( NULL, SPF_PURGEBEFORESPEAK, NULL );
m_bIsSpeaking = false;
ChangeSpeakButton(); }
if (m_cpVoice) { m_cpVoice->SetNotifySink(NULL); m_cpVoice.Release(); }
// Find out what is selected
int iSelIndex = (int) ::SendMessage( m_hTTSCombo, CB_GETCURSEL, 0, 0 ); m_cpCurVoiceToken = (ISpObjectToken *) ::SendMessage( m_hTTSCombo, CB_GETITEMDATA, iSelIndex, 0 ); if ( CB_ERR == (LRESULT) m_cpCurVoiceToken.p ) { m_cpCurVoiceToken = NULL; }
HRESULT hr = E_UNEXPECTED; if (m_cpCurVoiceToken) { BOOL fSupported = FALSE; hr = m_cpCurVoiceToken->IsUISupported(SPDUI_EngineProperties, NULL, 0, NULL, &fSupported); if ( FAILED( hr ) ) { fSupported = FALSE; } ::EnableWindow(::GetDlgItem(m_hDlg, IDC_TTS_ADV), fSupported);
// Get the voice from the SR Dlg's recognizer, if possible.
// Otherwise just CoCreate the voice
ISpRecoContext *pRecoContext = g_pSRDlg ? g_pSRDlg->GetRecoContext() : NULL; if ( pRecoContext ) { hr = pRecoContext->GetVoice( &m_cpVoice );
// Since the recocontext might not have changed, but we might have
// changed the default audio object, just to be sure, we
// go and get the default audio out token and SetOutput
CComPtr<ISpObjectToken> cpAudioToken; if ( SUCCEEDED( hr ) ) { hr = SpGetDefaultTokenFromCategoryId( SPCAT_AUDIOOUT, &cpAudioToken ); } else { hr = m_cpVoice.CoCreateInstance(CLSID_SpVoice); }
if ( SUCCEEDED( hr ) ) { hr = m_cpVoice->SetOutput( cpAudioToken, TRUE ); } } else { hr = m_cpVoice.CoCreateInstance(CLSID_SpVoice); }
if( SUCCEEDED( hr ) ) { hr = m_cpVoice->SetVoice(m_cpCurVoiceToken); } if (SUCCEEDED(hr)) { CComPtr<ISpNotifyTranslator> cpNotify; cpNotify.CoCreateInstance(CLSID_SpNotifyTranslator); cpNotify->InitSpNotifyCallback(this, 0, 0); m_cpVoice->SetInterest(SPFEI(SPEI_WORD_BOUNDARY) | SPFEI(SPEI_END_INPUT_STREAM), 0); m_cpVoice->SetNotifySink(cpNotify);
// Set the appropriate speed on the slider
if (fUsePersistentRate) { CComPtr<ISpObjectTokenCategory> cpCategory; ULONG ulCurRate=0; if (SUCCEEDED(SpGetCategoryFromId(SPCAT_VOICES, &cpCategory))) { CComPtr<ISpDataKey> cpDataKey; if (SUCCEEDED(cpCategory->GetDataKey(SPDKL_CurrentUser, &cpDataKey))) { cpDataKey->GetDWORD(SPVOICECATEGORY_TTSRATE, (ULONG*)&ulCurRate); } } m_iOriginalRateSliderPos = ulCurRate; } else { m_iOriginalRateSliderPos = m_iSpeed; }
HWND hSlider = ::GetDlgItem( m_hDlg, IDC_SLIDER_SPEED ); ::SendMessage( hSlider, TBM_SETPOS, true, m_iOriginalRateSliderPos );
// Enable the Preview Voice button
::EnableWindow( ::GetDlgItem( g_pTTSDlg->GetHDlg(), IDC_SPEAK ), TRUE );
} else { // Warn the user of failure
WCHAR szError[MAX_LOADSTRING]; szError[0] = 0; LoadString( _Module.GetResourceInstance(), IDS_TTS_ERROR, szError, MAX_LOADSTRING); MessageBox( GetHDlg(), szError, m_szCaption, MB_ICONEXCLAMATION | g_dwIsRTLLayout );
// Disable the Preview Voice button
::EnableWindow( ::GetDlgItem( GetHDlg(), IDC_SPEAK ), FALSE ); }
// Put text corresponding to this voice into the edit control
PopulateEditCtrl(m_cpCurVoiceToken); // Kick the UI
KickCPLUI(); }
return hr; } /* CTTSDlg::DefaultVoiceChange */
/*****************************************************************************
* CTTSDlg::SetCheckmark * *-----------------------* * Description: * Sets the specified item in the list control to be either checked * or unchecked (as the default voice) ******************************************************************************/ void CTTSDlg::SetCheckmark( HWND hList, int iIndex, bool bCheck ) { m_fForceCheckStateChange = true; ListView_SetCheckState( hList, iIndex, bCheck );
m_fForceCheckStateChange = false; } /* CTTSDlg::SetCheckmark */
/*****************************************************************************
* CTTSDlg::KickCPLUI * *--------------------* * Description: * Determines if there is anything to apply right now ******************************************************************************/ void CTTSDlg::KickCPLUI() { bool fChanged = false;
// Compare IDs
CSpDynamicString dstrSelTokenID; CSpDynamicString dstrOriginalDefaultTokenID; HRESULT hr = E_FAIL; if ( m_cpOriginalDefaultVoiceToken && m_cpCurVoiceToken ) { hr = m_cpCurVoiceToken->GetId( &dstrSelTokenID ); } if ( SUCCEEDED( hr ) ) { hr = m_cpOriginalDefaultVoiceToken->GetId( &dstrOriginalDefaultTokenID ); } if ( SUCCEEDED( hr ) && ( 0 != wcsicmp( dstrOriginalDefaultTokenID, dstrSelTokenID ) ) ) { fChanged = true; } // Check the audio device
if ( m_pAudioDlg && m_pAudioDlg->IsAudioDeviceChanged() ) { fChanged = true; }
// Check the voice rate
int iSpeed = (int) ::SendDlgItemMessage( m_hDlg, IDC_SLIDER_SPEED, TBM_GETPOS, 0, 0 ); if ( m_iOriginalRateSliderPos != iSpeed ) { fChanged = true; }
// Tell the main propsheet
HWND hwndParent = ::GetParent( m_hDlg ); ::SendMessage( hwndParent, fChanged ? PSM_CHANGED : PSM_UNCHANGED, (WPARAM)(m_hDlg), 0 );
} /* CTTSDlg::KickCPLUI */
/*****************************************************************************
* CTTSDlg::ChangeSpeed * *----------------------* * Description: * Called when the slider has moved. * Adjusts the speed of the voice ****************************************************************** BECKYW ***/ void CTTSDlg::ChangeSpeed() { HWND hSlider = ::GetDlgItem( m_hDlg, IDC_SLIDER_SPEED ); m_iSpeed = (int)::SendMessage( hSlider, TBM_GETPOS, 0, 0 ); m_cpVoice->SetRate( m_iSpeed );
KickCPLUI(); } /* CTTSDlg::ChangeSpeed */
/*****************************************************************************
* CTTSDlg::ChangeSpeakButton * *----------------------------* * Description: * Changes the text in the "Preview Voice" button in order to * reflect whether there is currently a speak happening. ****************************************************************** BECKYW ***/ void CTTSDlg::ChangeSpeakButton() { WCHAR pszButtonCaption[ MAX_LOADSTRING ]; HWND hButton = ::GetDlgItem( m_hDlg, IDC_SPEAK ); ::LoadString( _Module.GetResourceInstance(), m_bIsSpeaking ? IDS_STOP_PREVIEW : IDS_PREVIEW, pszButtonCaption, MAX_LOADSTRING ); ::SendMessage( hButton, WM_SETTEXT, 0, (LPARAM) pszButtonCaption );
if (!m_bIsSpeaking) { ::SetFocus(GetDlgItem(m_hDlg, IDC_SPEAK)); } } /* CTTSDlg::ChangeSpeakButton */
/*****************************************************************************
* CTTSDlg::Speak * *----------------* * Description: * Speaks the contents of the edit control. * If it is already speaking, shuts up. ****************************************************************** BECKYW ***/ void CTTSDlg::Speak() { SPDBG_FUNC( "CTTSDlg::Speak" ); if ( m_bIsSpeaking ) { // This forces the voice to stop speaking
m_cpVoice->Speak( NULL, SPF_PURGEBEFORESPEAK, NULL );
m_bIsSpeaking = false;
ChangeSpeakButton(); } else { ChangeSpeed();
GETTEXTEX gtex = { sp_countof(m_wszCurSpoken), GT_DEFAULT, 1200, NULL, NULL }; m_wszCurSpoken[0] = 0; LRESULT cChars = ::SendDlgItemMessage(m_hDlg, IDC_EDIT_SPEAK, EM_GETTEXTEX, (WPARAM)>ex, (LPARAM)m_wszCurSpoken);
if (cChars) { HRESULT hr = m_cpVoice->Speak(m_wszCurSpoken, SPF_ASYNC | SPF_PURGEBEFORESPEAK, NULL); if ( SUCCEEDED( hr ) ) { m_bIsSpeaking = true;
::SetFocus(GetDlgItem(m_hDlg, IDC_EDIT_SPEAK)); ChangeSpeakButton(); } else { // Warn the user that he needs to apply the changes
WCHAR szError[MAX_LOADSTRING]; szError[0] = 0; LoadString( _Module.GetResourceInstance(), IDS_SPEAK_ERROR, szError, MAX_LOADSTRING); MessageBox( GetHDlg(), szError, m_szCaption, MB_ICONWARNING | g_dwIsRTLLayout ); } } }
} /* CTTSDlg::Speak */
/*****************************************************************************
* CTTSDlg::StopSpeak * *----------------* * Description: * Stops the voice from speaking. * If it is already speaking, shuts up. ****************************************************************** BECKYW ***/ void CTTSDlg::StopSpeak() { SPDBG_FUNC( "CTTSDlg::StopSpeak" ); if ( m_bIsSpeaking ) { // This forces the voice to stop speaking
m_cpVoice->Speak( NULL, SPF_PURGEBEFORESPEAK, NULL );
m_bIsSpeaking = false; }
ChangeSpeakButton(); } /* CTTSDlg::StopSpeak */
/*****************************************************************************
* CTTSDlg::NotifyCallback * *-------------------------* * Description: * Callback function that highlights words as they are spoken. ****************************************************************** MIKEAR ***/ STDMETHODIMP CTTSDlg::NotifyCallback(WPARAM /* wParam */, LPARAM /* lParam */) { SPDBG_FUNC( "CTTSDlg::NotifyCallback" );
SPVOICESTATUS Stat; m_cpVoice->GetStatus(&Stat, NULL); WPARAM nStart; LPARAM nEnd; if (Stat.dwRunningState & SPRS_DONE) { nStart = nEnd = 0; m_bIsSpeaking = false; ChangeSpeakButton();
// Set the selection to an IP at the start of the text to speak
::SendDlgItemMessage( m_hDlg, IDC_EDIT_SPEAK, EM_SETSEL, 0, 0 ); } else { nStart = (LPARAM)Stat.ulInputWordPos; nEnd = nStart + Stat.ulInputWordLen; CHARRANGE cr; cr.cpMin = (LONG)nStart; cr.cpMax = (LONG)nEnd; ::SendDlgItemMessage( m_hDlg, IDC_EDIT_SPEAK, EM_EXSETSEL, 0, (LPARAM) &cr ); }
return S_OK; } /* CTTSDlg::NotifyCallback */
|