📄 texttospeech.cpp
字号:
// TextToSpeech.cpp: implementation of the CASRTextToSpeech class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "TextToSpeech.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CASRTextToSpeech::CASRTextToSpeech()
{
m_IpVoice = NULL;
m_cpOutAudio=NULL;
m_strLastError=_T("");
}
CASRTextToSpeech::~CASRTextToSpeech()
{
if(m_IpVoice)
{
Close();
}
}
// initialize
HRESULT CASRTextToSpeech::Init(HWND hWnd)
{
if (FAILED(CoInitialize(NULL)))
{
m_strLastError=_T("Error intialization COM");
return FALSE;
}
HRESULT hr;
hr = m_IpVoice.CoCreateInstance(CLSID_SpVoice);
if (FAILED(hr))
{
m_strLastError=_T("Error creating voice");
return hr;
}
// hr = m_IpVoice->SetInterest(SPFEI(SPEI_VISEME), SPFEI(SPEI_VISEME));
hr = m_IpVoice->SetInterest(SPFEI_ALL_TTS_EVENTS, SPFEI_ALL_TTS_EVENTS); //触发所有TTS事件
if (FAILED(hr))
{
m_strLastError=_T("Error creating interest...seriously");
return hr;
}
if (::IsWindow(hWnd))
{
hr = m_IpVoice->SetNotifyWindowMessage(hWnd, WM_SPEVENT, 0, 0); //设置事件到达所通知的窗口
if (FAILED(hr))
{
m_strLastError=_T("Error setting notification window");
return FALSE;
}
}
else
{
m_strLastError=_T("Error no this window");
return FALSE;
}
SpCreateDefaultObjectFromCategoryId( SPCAT_AUDIOOUT, &m_cpOutAudio ); //创建默认的音频输出
return TRUE;
}
void CASRTextToSpeech::Close()
{
if(m_IpVoice)
{
m_IpVoice.Release();
m_IpVoice=NULL;
}
if(m_cpOutAudio)
{
m_cpOutAudio.Release();
m_cpOutAudio=NULL;
}
CoUninitialize();
}
// speak
HRESULT CASRTextToSpeech::Speak(const WCHAR *pwcs,DWORD dwFlags,ULONG *pulStreamNumber)
{
HRESULT hr=m_IpVoice->Speak(pwcs,dwFlags,pulStreamNumber);
if (FAILED(hr))
{
switch (hr)
{
case E_INVALIDARG:
m_strLastError=_T("One or more parameters are invalid");
break;
case E_POINTER:
m_strLastError=_T("Invalid pointer");
break;
case E_OUTOFMEMORY:
m_strLastError=_T("Exceeded available memory");
break;
case SPERR_INVALID_FLAGS:
m_strLastError=_T("Invalid flags specified for this operation");
break;
case SPERR_DEVICE_BUSY:
m_strLastError=_T("Timeout occurred on synchronous call");
break;
}
}
return hr;
}
HRESULT CASRTextToSpeech::SpeakText(CString strText, DWORD dwFlags)
{
return Speak(strText.AllocSysString(),dwFlags,NULL);
}
HRESULT CASRTextToSpeech::SpeakXML(CString strXMLText, DWORD dwFlags)
{
return Speak(strXMLText.AllocSysString(),dwFlags,NULL);
}
HRESULT CASRTextToSpeech::SpeakTextFile(CString strTextPath)
{
return Speak(strTextPath.AllocSysString(),SPF_IS_FILENAME|SPF_ASYNC|SPF_IS_NOT_XML,NULL);
}
HRESULT CASRTextToSpeech::SpeakXMLFile(CString strXMLPath)
{
return Speak(strXMLPath.AllocSysString(),SPF_IS_FILENAME|SPF_ASYNC|SPF_IS_XML,NULL);
}
HRESULT CASRTextToSpeech::SpeakFromWAV(CString strPathWAV)
{
CComPtr<ISpStream> cpWavStream;
HRESULT hr = SPBindToFile( strPathWAV, SPFM_OPEN_READONLY, &cpWavStream );
if( SUCCEEDED( hr ))
{
hr = m_IpVoice->SpeakStream( cpWavStream, SPF_ASYNC, NULL );
}
m_IpVoice->WaitUntilDone( INFINITE );
cpWavStream.Release();
return hr;
}
HRESULT CASRTextToSpeech::SpeakToWAV(CString strText,CString strPathWAV)
{
CComPtr<ISpStreamFormat> cpOldStream;
CComPtr<ISpStream> cpWavStream;
CSpStreamFormat OriginalFmt;
HRESULT hr;
hr = m_IpVoice->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(strPathWAV, 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_IpVoice->SetOutput(cpWavStream, TRUE);
}
if ( SUCCEEDED( hr ) )
{
// Do the Speak
SpeakText(strText);
}
// 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_IpVoice->WaitUntilDone( INFINITE );
cpWavStream.Release();
// Reset output
m_IpVoice->SetOutput( cpOldStream, FALSE );
cpOldStream.Release();
return hr;
}
HRESULT CASRTextToSpeech::Pause()
{
return m_IpVoice->Pause();
}
HRESULT CASRTextToSpeech::Resume()
{
return m_IpVoice->Resume();
}
// rate
HRESULT CASRTextToSpeech::SetRate(long lRateAdjust)
{
return m_IpVoice->SetRate(lRateAdjust);
}
HRESULT CASRTextToSpeech::GetRate(long* plRateAdjust)
{
return m_IpVoice->GetRate(plRateAdjust);
}
// volume
HRESULT CASRTextToSpeech::SetVolume(USHORT usVolume)
{
return m_IpVoice->SetVolume(usVolume);
}
HRESULT CASRTextToSpeech::GetVolume(USHORT* pusVolume)
{
return m_IpVoice->GetVolume(pusVolume);
}
// voice
ULONG CASRTextToSpeech::GetVoiceCount()
{
HRESULT hr = S_OK;
CComPtr<ISpObjectToken> cpVoiceToken;
CComPtr<IEnumSpObjectTokens> cpEnum;
ULONG ulCount = -1;
//Enumerate the available voices
hr = SpEnumTokens(SPCAT_VOICES, NULL, NULL, &cpEnum);
if(FAILED(hr))
{
m_strLastError = _T("Error to enumerate voices");
return -1;
}
//Get the number of voices
hr = cpEnum->GetCount(&ulCount);
if(FAILED(hr))
{
m_strLastError = _T("Error to get voice count");
return -1;
}
return ulCount;
}
HRESULT CASRTextToSpeech::GetVoice(WCHAR **ppszDescription, ULONG lIndex)
{
HRESULT hr = S_OK;
CComPtr<ISpObjectToken> cpVoiceToken;
CComPtr<IEnumSpObjectTokens> cpEnum;
ULONG ulCount = 0;
if (lIndex == -1)
{
// current voice
//
hr = m_IpVoice->GetVoice(&cpVoiceToken);
if(FAILED(hr))
{
m_strLastError = _T("Error to get current voice");
return hr;
}
SpGetDescription(cpVoiceToken, ppszDescription);
if(FAILED(hr))
{
m_strLastError = _T("Error to get current voice description");
return hr;
}
}
else
{
// else other voices, we should enumerate the voice list first
//
//Enumerate the available voices
hr = SpEnumTokens(SPCAT_VOICES, NULL, NULL, &cpEnum);
if(FAILED(hr))
{
m_strLastError = _T("Error to enumerate voices");
return hr;
}
//Get the number of voices
hr = cpEnum->GetCount(&ulCount);
if(FAILED(hr))
{
m_strLastError = _T("Error to voice count");
return hr;
}
// range control
ASSERT(lIndex >= 0);
ASSERT(lIndex < ulCount);
// Obtain specified voice id
ULONG l = 0;
while (SUCCEEDED(hr))
{
cpVoiceToken.Release();
hr = cpEnum->Next( 1, &cpVoiceToken, NULL );
if(FAILED(hr))
{
m_strLastError = _T("Error to get voice token");
return hr;
}
if (l == lIndex)
{
hr = SpGetDescription(cpVoiceToken, ppszDescription);
if(FAILED(hr))
{
m_strLastError = _T("Error to get voice description");
return hr;
}
break;
}
l++;
}
}
return hr;
}
HRESULT CASRTextToSpeech::SetVoice(WCHAR **ppszDescription)
{
HRESULT hr = S_OK;
CComPtr<ISpObjectToken> cpVoiceToken;
CComPtr<IEnumSpObjectTokens> cpEnum;
ULONG ulCount = 0;
//Enumerate the available voices
hr = SpEnumTokens(SPCAT_VOICES, NULL, NULL, &cpEnum);
if(FAILED(hr))
{
m_strLastError = _T("Error to enumerate voices");
return hr;
}
//Get the number of voices
hr = cpEnum->GetCount(&ulCount);
if(FAILED(hr))
{
m_strLastError = _T("Error to voice count");
return hr;
}
// Obtain specified voice id
while (SUCCEEDED(hr) && ulCount--)
{
cpVoiceToken.Release();
hr = cpEnum->Next( 1, &cpVoiceToken, NULL );
if(FAILED(hr))
{
m_strLastError = _T("Error to voice token");
return hr;
}
WCHAR *pszDescription1;
hr = SpGetDescription(cpVoiceToken, &pszDescription1);
if(FAILED(hr))
{
m_strLastError = _T("Error to get voice description");
return hr;
}
if (! wcsicmp(pszDescription1, *ppszDescription))
{
hr = m_IpVoice->SetVoice(cpVoiceToken);
if(FAILED(hr))
{
m_strLastError = _T("Error to set voice");
return hr;
}
break;
}
}
return hr;
}
HRESULT CASRTextToSpeech::SetAudio(SPSTREAMFORMAT eFmt)
{
HRESULT hr;
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_IpVoice->SetOutput( m_cpOutAudio, FALSE );
}
return hr;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -