加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
dlgmain.cpp 33.86 KB
一键复制 编辑 原始数据 按行查看 历史
unknown 提交于 2015-07-14 02:29 . First commit
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270
/////////////////////////////////////////////////////////////////////////
// DlgMain.CPP
//
// This module handles the main dialog box, and it's controls.
//
// The main dialog is actually the application's main window class, there
// is no conventional "Window class", only this dialog class which is
// registered in WinMain.
//
// Revision History:
// 06/06/99 Created
//Copyright (c) Microsoft Corporation. All rights reserved.
//
/////////////////////////////////////////////////////////////////////////
#include "globals.h"
//
// Other local functions
//
inline void TTSAppStatusMessage(HWND hWnd, TCHAR* szMessage);
// ---------------------------------------------------------------------------
// CTTSApp::DlgProcMain
// ---------------------------------------------------------------------------
// Description: Main window procedure.
// Arguments:
// HWND [in] Window handle.
// UINT [in] Message identifier.
// WPARAM [in] Depends on message.
// LPARAM [in] Depends on message.
// Returns:
// LPARAM Depends on message.
LRESULT CALLBACK CTTSApp::DlgProcMain(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CTTSApp* pThis = (CTTSApp *)::GetWindowLong(hwnd, GWL_USERDATA);
// Call the appropriate function to handle the messages
switch (uMsg)
{
case WM_INITDIALOG:
::SetWindowLong(hwnd, GWL_USERDATA, lParam);
pThis = (CTTSApp *)lParam;
return pThis->OnInitDialog(hwnd);
case WM_TTSAPPCUSTOMEVENT:
pThis->MainHandleSynthEvent();
return TRUE;
case WM_HSCROLL:
pThis->HandleScroll((HWND)lParam);
return TRUE;
case WM_COMMAND:
if (pThis)
{
pThis->MainHandleCommand((int)(LOWORD(wParam)), (HWND)lParam,
(UINT)HIWORD(wParam));
}
return TRUE;
case WM_CLOSE:
pThis->MainHandleClose();
return TRUE;
}
return FALSE;
}
/////////////////////////////////////////////////////////////////
HRESULT CTTSApp::InitSapi()
/////////////////////////////////////////////////////////////////
{
HRESULT hr;
hr = m_cpVoice.CoCreateInstance(CLSID_SpVoice);
return hr;
}
/////////////////////////////////////////////////////////////////
HIMAGELIST CTTSApp::InitImageList()
/////////////////////////////////////////////////////////////////
{
HIMAGELIST hListBmp;
HBITMAP hBmp;
hListBmp = ImageList_Create(CHARACTER_WIDTH, CHARACTER_HEIGHT, ILC_COLOR32 | ILC_MASK, 1, 0);
if (hListBmp)
{
hBmp = LoadBitmap(m_hInst, MAKEINTRESOURCE(IDB_MICFULL));
ImageList_AddMasked(hListBmp, hBmp, 0xff00ff);
DeleteObject(hBmp);
hBmp = LoadBitmap(m_hInst, MAKEINTRESOURCE(IDB_MICMOUTH2));
ImageList_AddMasked(hListBmp, hBmp, 0xff00ff);
DeleteObject(hBmp);
hBmp = LoadBitmap(m_hInst, MAKEINTRESOURCE(IDB_MICMOUTH3));
ImageList_AddMasked(hListBmp, hBmp, 0xff00ff);
DeleteObject(hBmp);
hBmp = LoadBitmap(m_hInst, MAKEINTRESOURCE(IDB_MICMOUTH4));
ImageList_AddMasked(hListBmp, hBmp, 0xff00ff);
DeleteObject(hBmp);
hBmp = LoadBitmap(m_hInst, MAKEINTRESOURCE(IDB_MICMOUTH5));
ImageList_AddMasked(hListBmp, hBmp, 0xff00ff);
DeleteObject(hBmp);
hBmp = LoadBitmap(m_hInst, MAKEINTRESOURCE(IDB_MICMOUTH6));
ImageList_AddMasked(hListBmp, hBmp, 0xff00ff);
DeleteObject(hBmp);
hBmp = LoadBitmap(m_hInst, MAKEINTRESOURCE(IDB_MICMOUTH7));
ImageList_AddMasked(hListBmp, hBmp, 0xff00ff);
DeleteObject(hBmp);
hBmp = LoadBitmap(m_hInst, MAKEINTRESOURCE(IDB_MICMOUTH8));
ImageList_AddMasked(hListBmp, hBmp, 0xff00ff);
DeleteObject(hBmp);
hBmp = LoadBitmap(m_hInst, MAKEINTRESOURCE(IDB_MICMOUTH9));
ImageList_AddMasked(hListBmp, hBmp, 0xff00ff);
DeleteObject(hBmp);
hBmp = LoadBitmap(m_hInst, MAKEINTRESOURCE(IDB_MICMOUTH10));
ImageList_AddMasked(hListBmp, hBmp, 0xff00ff);
DeleteObject(hBmp);
hBmp = LoadBitmap(m_hInst, MAKEINTRESOURCE(IDB_MICMOUTH11));
ImageList_AddMasked(hListBmp, hBmp, 0xff00ff);
DeleteObject(hBmp);
hBmp = LoadBitmap(m_hInst, MAKEINTRESOURCE(IDB_MICMOUTH12));
ImageList_AddMasked(hListBmp, hBmp, 0xff00ff);
DeleteObject(hBmp);
hBmp = LoadBitmap(m_hInst, MAKEINTRESOURCE(IDB_MICMOUTH13));
ImageList_AddMasked(hListBmp, hBmp, 0xff00ff);
DeleteObject(hBmp);
hBmp = LoadBitmap(m_hInst, MAKEINTRESOURCE(IDB_MICEYESNAR));
ImageList_AddMasked(hListBmp, hBmp, 0xff00ff);
DeleteObject(hBmp);
hBmp = LoadBitmap(m_hInst, MAKEINTRESOURCE(IDB_MICEYESCLO));
ImageList_AddMasked(hListBmp, hBmp, 0xff00ff);
DeleteObject(hBmp);
ImageList_SetOverlayImage(hListBmp, 1, 1);
ImageList_SetOverlayImage(hListBmp, 2, 2);
ImageList_SetOverlayImage(hListBmp, 3, 3);
ImageList_SetOverlayImage(hListBmp, 4, 4);
ImageList_SetOverlayImage(hListBmp, 5, 5);
ImageList_SetOverlayImage(hListBmp, 6, 6);
ImageList_SetOverlayImage(hListBmp, 7, 7);
ImageList_SetOverlayImage(hListBmp, 8, 8);
ImageList_SetOverlayImage(hListBmp, 9, 9);
ImageList_SetOverlayImage(hListBmp, 10, 10);
ImageList_SetOverlayImage(hListBmp, 11, 11);
ImageList_SetOverlayImage(hListBmp, 12, 12);
ImageList_SetOverlayImage(hListBmp, 13, 13);
ImageList_SetOverlayImage(hListBmp, 14, WEYESNAR);
ImageList_SetOverlayImage(hListBmp, 15, WEYESCLO);
}
return hListBmp;
}
/////////////////////////////////////////////////////////////////
BOOL CTTSApp::OnInitDialog(HWND hWnd)
/////////////////////////////////////////////////////////////////
{
HRESULT hr = S_OK;
// Store this as the "Main Dialog"
m_hWnd = hWnd;
// Add some default text to the main edit control
SetDlgItemText(hWnd, IDE_EDITBOX, _T("Enter text you wish spoken here."));
// Set the event mask in the rich edit control so that it notifies us when text is
// changed in the control
SendMessage(GetDlgItem(hWnd, IDE_EDITBOX), EM_SETEVENTMASK, 0, ENM_CHANGE);
// Initialize the Output Format combo box
for (int i = 0; i < NUM_OUTPUTFORMATS; i++)
{
SendDlgItemMessage(hWnd, IDC_COMBO_OUTPUT, CB_ADDSTRING, 0,
(LPARAM)g_aszOutputFormat[i]);
SendDlgItemMessage(hWnd, IDC_COMBO_OUTPUT, CB_SETITEMDATA, i,
(LPARAM)g_aOutputFormat[i]);
}
if (!m_cpVoice)
{
hr = E_FAIL;
}
// Set the default output format as the current selection.
if (SUCCEEDED(hr))
{
CComPtr<ISpStreamFormat> cpStream;
HRESULT hrOutputStream = m_cpVoice->GetOutputStream(&cpStream);
if (hrOutputStream == S_OK)
{
CSpStreamFormat Fmt;
hr = Fmt.AssignFormat(cpStream);
if (SUCCEEDED(hr))
{
SPSTREAMFORMAT eFmt = Fmt.ComputeFormatEnum();
for (int i = 0; i < NUM_OUTPUTFORMATS; i++)
{
if (g_aOutputFormat[i] == eFmt)
{
m_DefaultFormatIndex = i;
SendDlgItemMessage(hWnd, IDC_COMBO_OUTPUT, CB_SETCURSEL, m_DefaultFormatIndex, 0);
}
}
}
}
else
{
SendDlgItemMessage(hWnd, IDC_COMBO_OUTPUT, CB_SETCURSEL, 0, 0);
}
}
// Use the SAPI5 helper function in sphelper.h to initialize the Voice combo box.
if (SUCCEEDED(hr))
{
hr = SpInitTokenComboBox(GetDlgItem(hWnd, IDC_COMBO_VOICES), SPCAT_VOICES);
}
if (SUCCEEDED(hr))
{
SpCreateDefaultObjectFromCategoryId(SPCAT_AUDIOOUT, &m_cpOutAudio);
}
// Set default voice data
VoiceChange();
// Set Range for Skip Edit Box...
SendDlgItemMessage(hWnd, IDC_SKIP_SPIN, UDM_SETRANGE, TRUE, MAKELONG(50, -50));
// Set the notification message for the voice
if (SUCCEEDED(hr))
{
m_cpVoice->SetNotifyWindowMessage(hWnd, WM_TTSAPPCUSTOMEVENT, 0, 0);
}
// We're interested in all TTS events
if (SUCCEEDED(hr))
{
hr = m_cpVoice->SetInterest(SPFEI_ALL_TTS_EVENTS, SPFEI_ALL_TTS_EVENTS);
}
// Get default rate and volume
if (SUCCEEDED(hr))
{
hr = m_cpVoice->GetRate(&m_DefaultRate);
// initialize sliders and edit boxes with default rate
if (SUCCEEDED(hr))
{
SendDlgItemMessage(hWnd, IDC_RATE_SLIDER, TBM_SETRANGE, TRUE, MAKELONG(SPMIN_RATE, SPMAX_RATE));
SendDlgItemMessage(hWnd, IDC_RATE_SLIDER, TBM_SETPOS, TRUE, m_DefaultRate);
SendDlgItemMessage(hWnd, IDC_RATE_SLIDER, TBM_SETPAGESIZE, TRUE, 5);
}
}
if (SUCCEEDED(hr))
{
hr = m_cpVoice->GetVolume(&m_DefaultVolume);
// initialize sliders and edit boxes with default volume
if (SUCCEEDED(hr))
{
SendDlgItemMessage(hWnd, IDC_VOLUME_SLIDER, TBM_SETRANGE, TRUE, MAKELONG(SPMIN_VOLUME, SPMAX_VOLUME));
SendDlgItemMessage(hWnd, IDC_VOLUME_SLIDER, TBM_SETPOS, TRUE, m_DefaultVolume);
SendDlgItemMessage(hWnd, IDC_VOLUME_SLIDER, TBM_SETPAGESIZE, TRUE, 10);
}
}
// If any SAPI initialization failed, shut down!
if (FAILED(hr))
{
MessageBox(NULL, _T("Error initializing speech objects. Shutting down."), _T("Error"), MB_OK);
SendMessage(hWnd, WM_CLOSE, 0, 0);
return(FALSE);
}
else
{
//
// Create the child windows to which we'll blit our result
//
HWND hCharWnd = GetDlgItem(hWnd, IDC_CHARACTER);
RECT rc;
GetClientRect(hCharWnd, &rc);
rc.left = (rc.right - CHARACTER_WIDTH) / 2;
rc.top = (rc.bottom - CHARACTER_HEIGHT) / 2;
m_hChildWnd = CreateWindow(CHILD_CLASS, NULL,
WS_CHILDWINDOW | WS_VISIBLE,
rc.left, rc.top,
rc.left + CHARACTER_WIDTH, rc.top + CHARACTER_HEIGHT,
hCharWnd, NULL, m_hInst, NULL);
if (!m_hChildWnd)
{
MessageBox(hWnd, _T("Error initializing speech objects. Shutting down."), _T("Error"), MB_OK);
SendMessage(hWnd, WM_CLOSE, 0, 0);
return(FALSE);
}
else
{
// Load Mouth Bitmaps and use and ImageList since we'll blit the mouth
// and eye positions over top of the full image
g_hListBmp = InitImageList();
}
}
return(TRUE);
}
/////////////////////////////////////////////////////////////////
void CTTSApp::Stop()
/////////////////////////////////////////////////////////////////
//
// Resets global audio state to stopped, updates the Pause/Resume button
// and repaints the mouth in a closed position
//
{
// Stop current rendering with a PURGEBEFORESPEAK...
HRESULT hr = m_cpVoice->Speak(NULL, SPF_PURGEBEFORESPEAK, 0);
if (FAILED(hr))
{
TTSAppStatusMessage(m_hWnd, _T("Stop error\r\n"));
}
SetWindowText(GetDlgItem(m_hWnd, IDB_PAUSE), _T("Pause"));
m_bPause = FALSE;
m_bStop = TRUE;
// Mouth closed
g_iBmp = 0;
SendMessage(m_hChildWnd, WM_PAINT, 0, 0);
InvalidateRect(m_hChildWnd, NULL, FALSE);
}
/////////////////////////////////////////////////////////////////
void CTTSApp::MainHandleCommand(int id, HWND /*hWndControl*/, UINT codeNotify)
/////////////////////////////////////////////////////////////////
//
// Handle each of the WM_COMMAND messages that come in, and deal with
// them appropriately
//
{
//UINT cNumChar = 0;
HRESULT hr = S_OK;
TCHAR szAFileName[NORM_SIZE] = _T("");
static BOOL bIsUnicode = FALSE;
BOOL bWavFileOpened = FALSE;
int iFormat;
CComPtr<ISpStream> cpWavStream;
CComPtr<ISpStreamFormat> cpOldStream;
HWND hwndEdit;
BOOL bFileOpened = FALSE;
// Get handle to the main edit box
hwndEdit = GetDlgItem(m_hWnd, IDE_EDITBOX);
switch (id)
{
// About Box display
case IDC_ABOUT:
::DialogBox(m_hInst, (LPCTSTR)IDD_ABOUT, m_hWnd, (DLGPROC)About);
break;
// Any change to voices is sent to VoiceChange() function
case IDC_COMBO_VOICES:
if (codeNotify == CBN_SELCHANGE)
{
hr = VoiceChange();
}
if (FAILED(hr))
{
TTSAppStatusMessage(m_hWnd, _T("Error changing voices\r\n"));
}
break;
// If user wants to speak a file pop the standard windows open file
// dialog box and load the text into a global buffer (m_pszwFileText)
// which will be used when the user hits speak.
case IDB_OPEN:
bFileOpened = CallOpenFileDialog(szAFileName,
_T("TXT (*.txt)\0*.txt\0XML (*.xml)\0*.xml\0All Files (*.*)\0*.*\0"));
if (bFileOpened)
{
//DWORD dwFileSize = 0;
USES_CONVERSION;
wcscpy(m_szWFileName, T2W(szAFileName));
ReadTheFile(szAFileName, &bIsUnicode, &m_pszwFileText);
if (bIsUnicode)
{
// Unicode source
UpdateEditCtlW(m_pszwFileText);
}
else
{
// MBCS source
#ifdef _UNICODE
TCHAR *pszFileText = _tcsdup( m_pszwFileText );
#else
// We're compiling ANSI, so we need to convert the string to MBCS
// Note that a W2T may not be good here, since this string might
// be very big
TCHAR *pszFileText = NULL;
int iNeeded = ::WideCharToMultiByte(CP_ACP, 0, m_pszwFileText, -1, NULL, 0, NULL, NULL);
pszFileText = (TCHAR *) ::malloc(sizeof(TCHAR) * (iNeeded + 1));
::WideCharToMultiByte(CP_ACP, 0, m_pszwFileText, -1, pszFileText, iNeeded + 1, NULL, NULL);
#endif
if (pszFileText)
{
SetDlgItemText(m_hWnd, IDE_EDITBOX, pszFileText);
free(pszFileText);
}
}
}
else
{
wcscpy(m_szWFileName, L"");
}
// Always SetFocus back to main edit window so text highlighting will work
SetFocus(hwndEdit);
break;
// Handle speak
case IDB_SPEAK:
HandleSpeak();
break;
case IDB_PAUSE:
if (!m_bStop)
{
if (!m_bPause)
{
SetWindowText(GetDlgItem(m_hWnd, IDB_PAUSE), _T("Resume"));
// Pause the voice...
m_cpVoice->Pause();
m_bPause = TRUE;
TTSAppStatusMessage(m_hWnd, _T("Pause\r\n"));
}
else
{
SetWindowText(GetDlgItem(m_hWnd, IDB_PAUSE), _T("Pause"));
m_cpVoice->Resume();
m_bPause = FALSE;
}
}
SetFocus(hwndEdit);
break;
case IDB_STOP:
TTSAppStatusMessage(m_hWnd, _T("Stop\r\n"));
// Set the global audio state to stop
Stop();
SetFocus(hwndEdit);
break;
case IDB_SKIP:
{
SetFocus(hwndEdit);
int fSuccess = false;
int SkipNum = GetDlgItemInt(m_hWnd, IDC_SKIP_EDIT, &fSuccess, true);
ULONG ulGarbage = 0;
WCHAR szGarbage[] = L"Sentence";
if (fSuccess)
{
TTSAppStatusMessage(m_hWnd, _T("Skip\r\n"));
hr = m_cpVoice->Skip(szGarbage, SkipNum, &ulGarbage);
}
else
{
TTSAppStatusMessage(m_hWnd, _T("Skip failed\r\n"));
}
break;
}
case IDE_EDITBOX:
// Set the global audio state to stop if user has changed contents of edit control
if (codeNotify == EN_CHANGE)
{
Stop();
}
break;
case IDB_SPEAKWAV:
bWavFileOpened = CallOpenFileDialog(szAFileName,
_T("WAV (*.wav)\0*.wav\0All Files (*.*)\0*.*\0"));
// Speak the wav file using SpeakStream
if (bWavFileOpened)
{
CComPtr<ISpStream> cpWavStream;
WCHAR szwWavFileName[NORM_SIZE] = L"";;
USES_CONVERSION;
wcscpy(szwWavFileName, T2W(szAFileName));
// User helper function found in sphelper.h to open the wav file and
// get back an IStream pointer to pass to SpeakStream
hr = SPBindToFile(szwWavFileName, SPFM_OPEN_READONLY, &cpWavStream);
if (SUCCEEDED(hr))
{
hr = m_cpVoice->SpeakStream(cpWavStream, SPF_ASYNC, NULL);
}
if (FAILED(hr))
{
TTSAppStatusMessage(m_hWnd, _T("Speak error\r\n"));
}
}
break;
// Reset all values to defaults
case IDB_RESET:
TTSAppStatusMessage(m_hWnd, _T("Reset\r\n"));
SendDlgItemMessage(m_hWnd, IDC_VOLUME_SLIDER, TBM_SETPOS, TRUE, m_DefaultVolume);
SendDlgItemMessage(m_hWnd, IDC_RATE_SLIDER, TBM_SETPOS, TRUE, m_DefaultRate);
SendDlgItemMessage(m_hWnd, IDC_SAVETOWAV, BM_SETCHECK, BST_UNCHECKED, 0);
SendDlgItemMessage(m_hWnd, IDC_EVENTS, BM_SETCHECK, BST_UNCHECKED, 0);
SetDlgItemText(m_hWnd, IDE_EDITBOX, _T("Enter text you wish spoken here."));
// reset output format
SendDlgItemMessage(m_hWnd, IDC_COMBO_OUTPUT, CB_SETCURSEL, m_DefaultFormatIndex, 0);
SendMessage(m_hWnd, WM_COMMAND, MAKEWPARAM(IDC_COMBO_OUTPUT, CBN_SELCHANGE), 0);
// Change the volume and the rate to reflect what the UI says
HandleScroll(::GetDlgItem(m_hWnd, IDC_VOLUME_SLIDER));
HandleScroll(::GetDlgItem(m_hWnd, IDC_RATE_SLIDER));
SetFocus(hwndEdit);
break;
case IDC_COMBO_OUTPUT:
if (codeNotify == CBN_SELCHANGE)
{
// Get the audio output format and set it's GUID
iFormat = SendDlgItemMessage(m_hWnd, IDC_COMBO_OUTPUT, CB_GETCURSEL, 0, 0);
SPSTREAMFORMAT eFmt = (SPSTREAMFORMAT)SendDlgItemMessage(m_hWnd, IDC_COMBO_OUTPUT,
CB_GETITEMDATA, iFormat, 0);
CSpStreamFormat Fmt;
Fmt.AssignFormat(eFmt);
if (m_cpOutAudio)
{
hr = m_cpOutAudio->SetFormat(Fmt.FormatId(), Fmt.WaveFormatExPtr());
}
else
{
hr = E_FAIL;
}
if (SUCCEEDED(hr))
{
hr = m_cpVoice->SetOutput(m_cpOutAudio, FALSE);
}
if (FAILED(hr))
{
TTSAppStatusMessage(m_hWnd, _T("Format rejected\r\n"));
}
EnableSpeakButtons(SUCCEEDED(hr));
}
break;
case IDC_SAVETOWAV:
{
USES_CONVERSION;
TCHAR szFileName[256];
_tcscpy(szFileName, _T("\0"));
BOOL bFileOpened = CallSaveFileDialog(szFileName,
_T("WAV (*.wav)\0*.wav\0All Files (*.*)\0*.*\0"));
if (bFileOpened == FALSE) break;
wcscpy(m_szWFileName, T2W(szFileName));
CSpStreamFormat OriginalFmt;
hr = m_cpVoice->GetOutputStream(&cpOldStream);
if (hr == S_OK)
{
hr = OriginalFmt.AssignFormat(cpOldStream);
}
else
{
hr = E_FAIL;
}
// User SAPI helper function in sphelper.h to create a wav file
if (SUCCEEDED(hr))
{
hr = SPBindToFile(m_szWFileName, SPFM_CREATE_ALWAYS, &cpWavStream, &OriginalFmt.FormatId(), OriginalFmt.WaveFormatExPtr());
}
if (SUCCEEDED(hr))
{
// Set the voice's output to the wav file instead of the speakers
hr = m_cpVoice->SetOutput(cpWavStream, TRUE);
}
if (SUCCEEDED(hr))
{
// Do the Speak
HandleSpeak();
}
// Set output back to original stream
// Wait until the speak is finished if saving to a wav file so that
// the smart pointer cpWavStream doesn't get released before its
// finished writing to the wav.
m_cpVoice->WaitUntilDone(INFINITE);
cpWavStream.Release();
// Reset output
m_cpVoice->SetOutput(cpOldStream, FALSE);
TCHAR szTitle[MAX_PATH];
TCHAR szConfString[MAX_PATH];
if (SUCCEEDED(hr))
{
LoadString(m_hInst, IDS_SAVE_NOTIFY, szConfString, MAX_PATH);
LoadString(m_hInst, IDS_NOTIFY_TITLE, szTitle, MAX_PATH);
MessageBox(m_hWnd, szConfString, szTitle, MB_OK | MB_ICONINFORMATION);
}
else
{
LoadString(m_hInst, IDS_SAVE_ERROR, szConfString, MAX_PATH);
MessageBox(m_hWnd, szConfString, NULL, MB_ICONEXCLAMATION);
}
break;
}
}
return;
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CTTSApp::HandleSpeak()
/////////////////////////////////////////////////////////////////////////////////////////////
{
HWND hwndEdit;
HRESULT hr = S_OK;
WCHAR *szWTextString = NULL;
// Get handle to the main edit box
hwndEdit = GetDlgItem(m_hWnd, IDE_EDITBOX);
TTSAppStatusMessage(m_hWnd, _T("Speak\r\n"));
SetFocus(hwndEdit);
m_bStop = FALSE;
// only get the string if we're not paused
if (!m_bPause)
{
// Find the length of the string in the buffer
GETTEXTLENGTHEX gtlx;
gtlx.codepage = 1200;
gtlx.flags = GTL_DEFAULT;
LONG lTextLen = SendDlgItemMessage(m_hWnd, IDE_EDITBOX, EM_GETTEXTLENGTHEX, (WPARAM)&gtlx, 0);
szWTextString = new WCHAR[lTextLen + 1];
GETTEXTEX GetText;
GetText.cb = (lTextLen + 1) * sizeof(WCHAR);
GetText.codepage = 1200;
GetText.flags = GT_DEFAULT;
GetText.lpDefaultChar = NULL;
GetText.lpUsedDefChar = NULL;
// Get the string in a unicode buffer
SendDlgItemMessage(m_hWnd, IDE_EDITBOX, EM_GETTEXTEX, (WPARAM)&GetText, (LPARAM)szWTextString);
// do we speak or interpret the XML
hr = m_cpVoice->Speak(szWTextString, SPF_ASYNC | ((IsDlgButtonChecked(m_hWnd, IDC_SPEAKXML)) ? SPF_IS_XML : SPF_IS_NOT_XML), 0);
delete[] szWTextString;
if (FAILED(hr))
{
TTSAppStatusMessage(m_hWnd, _T("Speak error\r\n"));
}
}
m_bPause = FALSE;
SetWindowText(GetDlgItem(m_hWnd, IDB_PAUSE), _T("Pause"));
SetFocus(hwndEdit);
// Set state to run
hr = m_cpVoice->Resume();
if (FAILED(hr))
{
TTSAppStatusMessage(m_hWnd, _T("Speak error\r\n"));
}
}
/////////////////////////////////////////////////////////////////
void CTTSApp::MainHandleSynthEvent()
/////////////////////////////////////////////////////////////////
//
// Handles the WM_TTSAPPCUSTOMEVENT application defined message and all
// of it's appropriate SAPI5 events.
//
{
CSpEvent event; // helper class in sphelper.h for events that releases any
// allocated memory in it's destructor - SAFER than SPEVENT
SPVOICESTATUS Stat;
WPARAM nStart;
LPARAM nEnd;
//int i = 0;
HRESULT hr = S_OK;
while (event.GetFrom(m_cpVoice) == S_OK)
{
switch (event.eEventId)
{
case SPEI_START_INPUT_STREAM:
if (IsDlgButtonChecked(m_hWnd, IDC_EVENTS))
{
TTSAppStatusMessage(m_hWnd, _T("StartStream event\r\n"));
}
break;
case SPEI_END_INPUT_STREAM:
// Set global boolean stop to TRUE when finished speaking
m_bStop = TRUE;
// Highlight entire text
nStart = 0;
nEnd = SendDlgItemMessage(m_hWnd, IDE_EDITBOX, WM_GETTEXTLENGTH, 0, 0);
SendDlgItemMessage(m_hWnd, IDE_EDITBOX, EM_SETSEL, nStart, nEnd);
// Mouth closed
g_iBmp = 0;
InvalidateRect(m_hChildWnd, NULL, FALSE);
if (IsDlgButtonChecked(m_hWnd, IDC_EVENTS))
{
TTSAppStatusMessage(m_hWnd, _T("EndStream event\r\n"));
}
break;
case SPEI_VOICE_CHANGE:
if (IsDlgButtonChecked(m_hWnd, IDC_EVENTS))
{
TTSAppStatusMessage(m_hWnd, _T("Voicechange event\r\n"));
}
break;
case SPEI_TTS_BOOKMARK:
if (IsDlgButtonChecked(m_hWnd, IDC_EVENTS))
{
// Get the string associated with the bookmark
// and add the null terminator.
USES_CONVERSION;
TCHAR szBuff2[MAX_PATH] = _T("Bookmark event: ");
WCHAR *pwszEventString = new WCHAR[wcslen(event.String()) + 1];
if (pwszEventString)
{
wcscpy(pwszEventString, event.String());
_tcscat(szBuff2, W2T(pwszEventString));
delete[] pwszEventString;
}
_tcscat(szBuff2, _T("\r\n"));
TTSAppStatusMessage(m_hWnd, szBuff2);
}
break;
case SPEI_WORD_BOUNDARY:
hr = m_cpVoice->GetStatus(&Stat, NULL);
if (FAILED(hr))
{
TTSAppStatusMessage(m_hWnd, _T("Voice GetStatus error\r\n"));
}
// Highlight word
nStart = (LPARAM)(Stat.ulInputWordPos / sizeof(char));
nEnd = nStart + Stat.ulInputWordLen;
SendDlgItemMessage(m_hWnd, IDE_EDITBOX, EM_SETSEL, nStart, nEnd);
if (IsDlgButtonChecked(m_hWnd, IDC_EVENTS))
{
TTSAppStatusMessage(m_hWnd, _T("Wordboundary event\r\n"));
}
break;
case SPEI_PHONEME:
if (IsDlgButtonChecked(m_hWnd, IDC_EVENTS))
{
TTSAppStatusMessage(m_hWnd, _T("Phoneme event\r\n"));
}
break;
case SPEI_VISEME:
// Get the current mouth viseme position and map it to one of the
// 7 mouth bitmaps.
g_iBmp = g_aMapVisemeToImage[event.Viseme()]; // current viseme
InvalidateRect(m_hChildWnd, NULL, FALSE);
if (IsDlgButtonChecked(m_hWnd, IDC_EVENTS))
{
TTSAppStatusMessage(m_hWnd, _T("Viseme event\r\n"));
}
break;
case SPEI_SENTENCE_BOUNDARY:
if (IsDlgButtonChecked(m_hWnd, IDC_EVENTS))
{
TTSAppStatusMessage(m_hWnd, _T("Sentence event\r\n"));
}
break;
case SPEI_TTS_AUDIO_LEVEL:
if (IsDlgButtonChecked(m_hWnd, IDC_EVENTS))
{
USES_CONVERSION;
WCHAR wszBuff[MAX_PATH];
swprintf(wszBuff, MAX_PATH-1, L"Audio level: %d\r\n", (ULONG)event.wParam);
TTSAppStatusMessage(m_hWnd, W2T(wszBuff));
}
break;
case SPEI_TTS_PRIVATE:
if (IsDlgButtonChecked(m_hWnd, IDC_EVENTS))
{
TTSAppStatusMessage(m_hWnd, _T("Private engine event\r\n"));
}
break;
default:
TTSAppStatusMessage(m_hWnd, _T("Unknown message\r\n"));
break;
}
}
}
/////////////////////////////////////////////////////////////////////////
void CTTSApp::HandleScroll(HWND hCtl)
/////////////////////////////////////////////////////////////////////////
{
static long hpos = 1;
HWND hVolume, hRate;
HRESULT hr = S_OK;
// Get the current position of the slider
hpos = SendMessage(hCtl, TBM_GETPOS, 0, 0);
// Get the Handle for the scroll bar so it can be associated with an edit box
hVolume = GetDlgItem(m_hWnd, IDC_VOLUME_SLIDER);
hRate = GetDlgItem(m_hWnd, IDC_RATE_SLIDER);
if (hCtl == hVolume)
{
hr = m_cpVoice->SetVolume((USHORT)hpos);
}
else if (hCtl == hRate)
{
hr = m_cpVoice->SetRate(hpos);
}
if (FAILED(hr))
{
TTSAppStatusMessage(m_hWnd, _T("Error setting volume / rate\r\n"));
}
return;
}
/////////////////////////////////////////////////////////////////
void CTTSApp::MainHandleClose()
/////////////////////////////////////////////////////////////////
{
// Call helper functions from sphelper.h to destroy combo boxes
// created with SpInitTokenComboBox
SpDestroyTokenComboBox(GetDlgItem(m_hWnd, IDC_COMBO_VOICES));
// Terminate the app
PostQuitMessage(0);
// Return success
return;
}
/////////////////////////////////////////////////////////////////
HRESULT CTTSApp::VoiceChange()
/////////////////////////////////////////////////////////////////
//
// This function is called during initialization and whenever the
// selection for the voice combo box changes.
// It gets the token pointer associated with the voice.
// If the new voice is different from the one that's currently
// selected, it first stops any synthesis that is going on and
// sets the new voice on the global voice object.
//
{
HRESULT hr = S_OK;
//GUID* pguidAudioFormat = NULL;
//int iFormat = 0;
// Get the token associated with the selected voice
ISpObjectToken* pToken = SpGetCurSelComboBoxToken(GetDlgItem(m_hWnd, IDC_COMBO_VOICES));
//Determine if it is the current voice
CComPtr<ISpObjectToken> pOldToken;
hr = m_cpVoice->GetVoice(&pOldToken);
if (SUCCEEDED(hr))
{
if (pOldToken != pToken)
{
// Stop speaking. This is not necesary, for the next call to work,
// but just to show that we are changing voices.
hr = m_cpVoice->Speak(NULL, SPF_PURGEBEFORESPEAK, 0);
// And set the new voice on the global voice object
if (SUCCEEDED(hr))
{
hr = m_cpVoice->SetVoice(pToken);
}
}
}
EnableSpeakButtons(SUCCEEDED(hr));
return hr;
}
/////////////////////////////////////////////////////////////////
BOOL CTTSApp::CallOpenFileDialog(TCHAR* szFileName, TCHAR* szFilter)
/////////////////////////////////////////////////////////////////
//
// Display the open dialog box to retrieve the user-selected
// .txt or .xml file for synthisizing
{
OPENFILENAME ofn;
BOOL bRetVal = TRUE;
LONG lRetVal;
HKEY hkResult;
TCHAR szPath[256] = _T("");
DWORD size = 256;
// Open the last directory used by this app (stored in registry)
lRetVal = RegCreateKeyEx(HKEY_CURRENT_USER,
_T("SOFTWARE\\Microsoft\\Speech\\AppData\\TTSApp"), 0, NULL, 0,
KEY_ALL_ACCESS, NULL, &hkResult, NULL);
if (lRetVal == ERROR_SUCCESS)
{
RegQueryValueEx(hkResult, _T("TTSFiles"), NULL, NULL, (PBYTE)szPath, &size);
}
size_t ofnsize = (BYTE*)&ofn.lpTemplateName + sizeof(ofn.lpTemplateName) - (BYTE*)&ofn;
ZeroMemory(&ofn, ofnsize);
ofn.lStructSize = ofnsize;
ofn.hwndOwner = m_hWnd;
ofn.lpstrFilter = szFilter;
ofn.lpstrCustomFilter = NULL;
ofn.nFilterIndex = 1;
ofn.lpstrInitialDir = szPath;
ofn.lpstrFile = szFileName;
ofn.nMaxFile = 256;
ofn.lpstrTitle = NULL;
ofn.lpstrFileTitle = NULL;
ofn.lpstrDefExt = NULL;
ofn.Flags = OFN_FILEMUSTEXIST | OFN_READONLY | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
// Pop the dialog
bRetVal = GetOpenFileName(&ofn);
if (!bRetVal) return bRetVal;
// Write the directory path you're in to the registry
TCHAR pathstr[256] = _T("");
_tcscpy(pathstr, szFileName);
int i = 0;
while (pathstr[i] != NULL)
{
i++;
}
while (pathstr[i] != '\\')
{
i--;
}
pathstr[i] = NULL;
// Now write the string to the registry
RegSetValueEx(hkResult, _T("TTSFiles"), NULL, REG_EXPAND_SZ, (PBYTE)pathstr, _tcslen(pathstr) + 1);
RegCloseKey(hkResult);
return bRetVal;
}
/////////////////////////////////////////////////////////////////
BOOL CTTSApp::CallSaveFileDialog(TCHAR* szFileName, TCHAR* szFilter)
/////////////////////////////////////////////////////////////////
//
// Display the save dialog box to save the wav file
{
OPENFILENAME ofn;
BOOL bRetVal = TRUE;
LONG lRetVal;
HKEY hkResult;
TCHAR szPath[256] = _T("");
DWORD size = 256;
// Open the last directory used by this app (stored in registry)
lRetVal = RegCreateKeyEx(HKEY_CLASSES_ROOT, _T("PathTTSDataFiles"), 0, NULL, 0,
KEY_ALL_ACCESS, NULL, &hkResult, NULL);
if (lRetVal == ERROR_SUCCESS)
{
RegQueryValueEx(hkResult, _T("TTSFiles"), NULL, NULL, (PBYTE)szPath, &size);
RegCloseKey(hkResult);
}
size_t ofnsize = (BYTE*)&ofn.lpTemplateName + sizeof(ofn.lpTemplateName) - (BYTE*)&ofn;
ZeroMemory(&ofn, ofnsize);
ofn.lStructSize = ofnsize;
ofn.hwndOwner = m_hWnd;
ofn.lpstrFilter = szFilter;
ofn.lpstrCustomFilter = NULL;
ofn.nFilterIndex = 1;
ofn.lpstrInitialDir = szPath;
ofn.lpstrFile = szFileName;
ofn.nMaxFile = 256;
ofn.lpstrTitle = NULL;
ofn.lpstrFileTitle = NULL;
ofn.lpstrDefExt = _T("wav");
ofn.Flags = OFN_OVERWRITEPROMPT;
// Pop the dialog
bRetVal = GetSaveFileName(&ofn);
if (!bRetVal) return bRetVal;
if (ofn.Flags & OFN_EXTENSIONDIFFERENT)
{
_tcscat(szFileName, _T(".wav"));
}
// Write the directory path you're in to the registry
TCHAR pathstr[256] = _T("");
_tcscpy(pathstr, szFileName);
int i = 0;
while (pathstr[i] != NULL)
{
i++;
}
while (pathstr[i] != '\\')
{
i--;
}
pathstr[i] = NULL;
// Now write the string to the registry
lRetVal = RegCreateKeyEx(HKEY_CLASSES_ROOT, _T("PathTTSDataFiles"), 0, NULL, 0,
KEY_ALL_ACCESS, NULL, &hkResult, NULL);
if (lRetVal == ERROR_SUCCESS)
{
RegSetValueEx(hkResult, _T("TTSFiles"), NULL, REG_EXPAND_SZ, (PBYTE)pathstr, _tcslen(pathstr) + 1);
RegCloseKey(hkResult);
}
return bRetVal;
}
/////////////////////////////////////////////////////
HRESULT CTTSApp::ReadTheFile(TCHAR* szFileName, BOOL* bIsUnicode, WCHAR** ppszwBuff)
/////////////////////////////////////////////////////
//
// This file opens and reads the contents of a file. It
// returns a pointer to the string.
// Warning, this function allocates memory for the string on
// the heap so the caller must free it with 'delete'.
//
{
// Open up the file and copy it's contents into a buffer to return
HRESULT hr = 0;
HANDLE hFile;
DWORD dwSize = 0;
DWORD dwBytesRead = 0;
// First delete any memory previously allocated by this function
if (m_pszwFileText)
{
delete m_pszwFileText;
}
hFile = CreateFile(szFileName, GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
*ppszwBuff = NULL;
hr = E_FAIL;
}
if (SUCCEEDED(hr))
{
dwSize = GetFileSize(hFile, NULL);
if (dwSize == 0xffffffff)
{
*ppszwBuff = NULL;
hr = E_FAIL;
}
}
if (SUCCEEDED(hr))
{
// Read the file contents into a wide buffer and then determine
// if it's a unicode or ascii file
WCHAR Signature = 0;
ReadFile(hFile, &Signature, 2, &dwBytesRead, NULL);
// Check to see if its a unicode file by looking at the signature of the first character.
if (0xFEFF == Signature)
{
*ppszwBuff = new WCHAR[dwSize / 2];
*bIsUnicode = TRUE;
ReadFile(hFile, *ppszwBuff, dwSize - 2, &dwBytesRead, NULL);
(*ppszwBuff)[dwSize / 2 - 1] = NULL;
CloseHandle(hFile);
}
else // MBCS source
{
char* pszABuff = new char[dwSize + 1];
*ppszwBuff = new WCHAR[dwSize + 1];
*bIsUnicode = FALSE;
SetFilePointer(hFile, NULL, NULL, FILE_BEGIN);
ReadFile(hFile, pszABuff, dwSize, &dwBytesRead, NULL);
pszABuff[dwSize] = NULL;
::MultiByteToWideChar(CP_ACP, 0, pszABuff, -1, *ppszwBuff, dwSize + 1);
delete[] pszABuff;
CloseHandle(hFile);
}
}
return hr;
}
/////////////////////////////////////////////////////////////////////////
void CTTSApp::UpdateEditCtlW(WCHAR* pszwText)
/////////////////////////////////////////////////////////////////////////
{
CComPtr<IRichEditOle> cpRichEdit;
CComPtr<ITextDocument> cpTextDocument;
ITextServices* pTextServices = NULL;
HRESULT hr = S_OK;
// Use rich edit control interface pointers to update text
if (SendDlgItemMessage(m_hWnd, IDE_EDITBOX, EM_GETOLEINTERFACE, 0, (LPARAM)(LPVOID FAR *)&cpRichEdit))
{
hr = cpRichEdit.QueryInterface(&cpTextDocument);
if (SUCCEEDED(hr))
{
hr = cpTextDocument->QueryInterface(IID_ITextServices, (void**)&pTextServices);
}
if (SUCCEEDED(hr))
{
BSTR bstr = SysAllocString(pszwText);
hr = pTextServices->TxSetText(bstr);
pTextServices->Release();
SysFreeString(bstr);
}
}
// Add text the old fashon way by converting it to ansi. Note information
// loss will occur because of the WC2MB conversion.
if (!cpRichEdit || FAILED(hr))
{
USES_CONVERSION;
TCHAR *pszFileText = _tcsdup(W2T(pszwText));
SetDlgItemText(m_hWnd, IDE_EDITBOX, pszFileText);
free(pszFileText);
}
}
/////////////////////////////////////////////////////////////////////////
void CTTSApp::EnableSpeakButtons(BOOL fEnable)
/////////////////////////////////////////////////////////////////////////
{
::EnableWindow(::GetDlgItem(m_hWnd, IDB_SPEAK), fEnable);
::EnableWindow(::GetDlgItem(m_hWnd, IDB_PAUSE), fEnable);
::EnableWindow(::GetDlgItem(m_hWnd, IDB_STOP), fEnable);
::EnableWindow(::GetDlgItem(m_hWnd, IDB_SKIP), fEnable);
::EnableWindow(::GetDlgItem(m_hWnd, IDB_SPEAKWAV), fEnable);
::EnableWindow(::GetDlgItem(m_hWnd, IDC_SAVETOWAV), fEnable);
}
/////////////////////////////////////////////////////
inline void TTSAppStatusMessage(HWND hWnd, TCHAR* szMessage)
/////////////////////////////////////////////////////
//
// This function prints debugging messages to the debug edit control
//
{
static TCHAR szDebugText[MAX_SIZE] = _T("");
static int i = 0;
// Clear out the buffer after 100 lines of text have been written
// to the debug window since it can only hold 4096 characters.
if (i == 100)
{
_tcscpy(szDebugText, _T(""));
i = 0;
}
// Attach the new message to the ongoing list of messages
_tcscat(szDebugText, szMessage);
SetDlgItemText(hWnd, IDC_DEBUG, szDebugText);
SendDlgItemMessage(hWnd, IDC_DEBUG, EM_LINESCROLL, 0, i);
i++;
return;
}
/*****************************************************************************************
* About() *
*---------*
* Description:
* Message handler for the "About" box.
******************************************************************************************/
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lParam*/)
{
USES_CONVERSION;
switch (message)
{
case WM_COMMAND:
{
WORD wId = LOWORD(wParam);
//WORD wEvent = HIWORD(wParam);
switch (wId)
{
case IDOK:
case IDCANCEL:
EndDialog(hDlg, LOWORD(wParam));
return TRUE;
}
}
}
return FALSE;
} /* About */
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化