windows下文本转语音TTS库封装

一、文本转语音实现

本文提及的文本转语音库其实很多年前写的库,最近有才时间整理才将对应库整理成文章供各位网友参考。

其实在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技术快餐  
电子邮箱:[email protected]  

猜你喜欢

转载自blog.csdn.net/lixiang987654321/article/details/109008610