一、文本转语音实现
本文提及的文本转语音库其实很多年前写的库,最近有才时间整理才将对应库整理成文章供各位网友参考。
其实在windows下自带了文本转语音以及语音识别的功能,这里由于项目中需要将报警文本信息使用语音形式转化出来,所以这里仅仅只介绍文本转语音的使用。
windows下我们将文本转语音使用的就是windows自带的COM组件,也就是TTS库,为了方便使用我将语音转文本的库封装成一个Dll库,目的也是为了后续组件化和模块化后续系统的功能,该封装库仅有三个功能:
(1)语音库的初始化
//-------------------------------------------------
// 功 能:语音库的初始化
// 参 数:
// 无
// 返回值:
// 成功返回true反之返回false
// 说 明:
// 在系统初始化的时候调用该接口与TTS_UnInit对应
//-------------------------------------------------
TTSSDK_API bool TTS_Init();
(2)本文转语音
//-------------------------------------------------
// 功 能:语音库的初始化
// 参 数:
// 【输入】content : 需要播放的文字内容
// 【输入】size : 播放的文字内容长度
// 返回值:
// 无
// 说 明:
// 为了性能,该接口是异步接口调用后后台自动播放
//-------------------------------------------------
TTSSDK_API void TTS_Speak(const char* content,const int size);
(3)语音库的反初始化
//-------------------------------------------------
// 功 能:语音库的反初始化
// 参 数:
// 无
// 返回值:
// 无
// 说 明:
// 在系统退出的时候调用该接口以使用资源
//-------------------------------------------------
TTSSDK_API void TTS_UnInit();
我们将重点放在第三个接口的实现上:
TTSSDK_API void TTS_Speak(const char* content,const int size)
{
NetCommandPtr pCmd = boost::make_shared<CNetCommand>(boost::BOOST_BIND(CTtsInstance::Speek, content));
CMessageEngine::get_mutable_instance().Post(pCmd);
}
该接口的实现是通过异步方式调用的,这里使用了boost封装的消息处理引擎处理对应的消息,其目的是为了上层应用调用的完成之后可以继续做自己的工作,而不影响上层应用,特别是UI相关的线程,因为语音播放如果是同步播放的,10几个文字播放出来(包括标点符号的停顿)大概需要6-10秒,所以该接口是做成了异步处理。
最后,我们只需要关注TTS的实例的Speek方法实现就可以了:
bool CTtsInstance::Speek(std::string text)
{
// window 64[不能使用-只能调用32bit程序]
if (!CTtsInstance::Prepared())
{
TCHAR filePath[MAX_PATH] = { 0 };
GetModuleFileName(NULL, filePath, MAX_PATH);
_tcsrchr(filePath, _T('\\'))[1] = _T('\0');
_tcscat(filePath, _T("tts.exe"));
if (-1 == _taccess(filePath, 0))
return false;
STARTUPINFO si = { 0 };
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = /*STARTF_USESHOWWINDOW*/0;
si.wShowWindow = /*SW_SHOW*/SW_HIDE;
si.lpDesktop = NULL;
PROCESS_INFORMATION ProcessInformation;
ZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
if (CreateProcess(filePath, (LPTSTR)text.c_str(), NULL, NULL, FALSE,/*CREATE_NEW_CONSOLE*/0, NULL, NULL, &si, &ProcessInformation))
{
WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
::CloseHandle(ProcessInformation.hThread);
::CloseHandle(ProcessInformation.hProcess);
}
}
else
{
if (!m_pISpVoice)
return false;
// 开始进行朗读
BSTR content = _com_util::ConvertStringToBSTR(text.c_str());
HRESULT hr = m_pISpVoice->Speak(content, SPF_ASYNC, NULL);
SysFreeString(content);
}
return true;
}
这里做了两部分工作:
(1)播放文本
我们使用的是语音播放COM库ISpObjectToken实现的,该组件初始化如下:
bool CTtsInstance::Init()
{
// 初始化COM组件
if(FAILED(::CoInitialize(NULL)))
return false;
if(FAILED(m_pISpVoice.CoCreateInstance(CLSID_SpVoice)))
return false;
// 枚举所有语音Token
CComPtr<IEnumSpObjectTokens> cpEnum;
if(FAILED(SpEnumTokens(SPCAT_VOICES, L"", L"", &cpEnum)))
return false;
ISpObjectToken* pToken = NULL;
while (cpEnum->Next(1, &pToken, NULL) == S_OK)
{
CSpDynamicString dstrDesc;
HRESULT hr = SpGetDescription(pToken, &dstrDesc);
if (SUCCEEDED(hr))
{
m_vecISpToken.push_back(pToken);
}
}
SetVoice(2052);
return true;
}
播放的时候我们只需要调用Speak接口即可,注意因为用的是COM组件,所以字符串也需要转换为COM的BSTR字符串!
(2)64bit系统兼容
因为我的程序一般都做了32bit和64bit的windows程序(包括所有dll库),经过测试发现64bit系统是调用COM库的,但是为了兼容其他的64bit库和app调用,我也必须兼容64bit的应用,我想到的好的办法是:本地调用。
既然系统仅仅支持32bit调用,那么64bit的dll封装只需要调用32bit的库就可以了,但是64bit的dll是无法调用32bit的dll的,但是可以通过进程方式调用,所以我又将该库封装成了一个32bit的exe,然后64bit的dll调用32bit的exe。该调用如下:
TCHAR filePath[MAX_PATH] = { 0 };
GetModuleFileName(NULL, filePath, MAX_PATH);
_tcsrchr(filePath, _T('\\'))[1] = _T('\0');
_tcscat(filePath, _T("tts.exe"));
if (-1 == _taccess(filePath, 0))
return false;
STARTUPINFO si = { 0 };
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = /*STARTF_USESHOWWINDOW*/0;
si.wShowWindow = /*SW_SHOW*/SW_HIDE;
si.lpDesktop = NULL;
PROCESS_INFORMATION ProcessInformation;
ZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
if (CreateProcess(filePath, (LPTSTR)text.c_str(), NULL, NULL, FALSE,/*CREATE_NEW_CONSOLE*/0, NULL, NULL, &si, &ProcessInformation))
{
WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
::CloseHandle(ProcessInformation.hThread);
::CloseHandle(ProcessInformation.hProcess);
}
通过查找本地的tts.exe文件进行远程调用并传递参数给exe即可。
二、测试示例
测试实例代码如下:
int _tmain(int argc, _TCHAR* argv[])
{
if(!TTS_Init()){
printf("初始化语音引擎失败!\n");
}
while (true)
{
TTS_Speak("尊敬的李先生,早上好!", 0);
printf("尊敬的李先生,早上好!\n");
Sleep(5000);
}
TTS_UnInit();
return 0;
}
(1)32bit的程序测试如下
可以看到播放正常!
(2)64bit程序测试如下
可以看到64bit的dll被调用后播放也依然正常,只不过通过进程调用方式耗时稍微长一点,不过也只能通过这种方式兼容了。
三、注意问题
不同机器测试可以发现,阉割版本的win7是没有自带语音库的,所有初始化语音COM组件正常,所以我们需要修复语音库,这里需要32bit和64bit的语音修复库,另外也可以安装自己喜欢的语音播放库(我的是男士音,不好辨识!)
源码获取、合作、技术交流请获取如下联系方式:
QQ交流群:961179337
微信账号:lixiang6153
公众号:IT技术快餐
电子邮箱:lixx2048@163.com