微软TTS 使用

原创 2016年06月01日 15:30:51

Win7环境下测试。

首先安装 Windows Speech SDK,下载地址为:http://www.microsoft.com/download/en/details.aspx?id=10121SpeechSDK51.exe是简体中文语音引擎,SpeechSDK51LangPach.exe是中文男生语音库。

微软本身的语音库中文语音比较生硬,可以安装NeoSpeech的中文语音库。百度下直接搜索NeoSpeech的中文语音即可找到。

从网上找的代码封装成类,qt5.5,在简单环境下可以满足需求,直接上代码

头文件

<pre name="code" class="cpp">#include <QObject>
#include <QList>
#include <sapi.h>

class ISpVoice;
class ISpObjectToken;
class ISpAudio;
class SpFormat;
class SimpleTTS : public QObject
{
	Q_OBJECT

public:
	SimpleTTS(QObject *parent);
	~SimpleTTS();


	void speak(QString text);
	
	//当前的音频格式
	SPSTREAMFORMAT currFormat();
private:
	void initVoices();				//获取可用的语音库
	void initSpFormat();				//设置可用的音频格式

private:

	bool m_bTTSEnable;				//语音库是否可用
	ISpVoice* m_pVoice;			    //ms com tts 组件
	ISpAudio* m_pAudio;				//voice 所使用的 音频相关
	QList<ISpObjectToken*> m_voices;	//可用的语音库数组

	QList<SpFormat> m_spFmts;       //
};



class SpFormat						//封装ms描述的音频格式
{
public:
	SpFormat(SPSTREAMFORMAT vl, QString sz);
	~SpFormat(){};

	QString discription()const { return m_discription; }
	SPSTREAMFORMAT getFormat()const{ return m_val; }
	DWORD rate()const{ return m_bytePS; }
private:
	SPSTREAMFORMAT m_val;
	QString m_discription;
	DWORD m_bytePS;
};



cpp

#include "simpletts.h"
#include <conio.h>
#include <sphelper.h>
#include <vector>
#include <queue>
#include <string>
#include <QDebug>
#include "jiontctrllmgr.h"
#include "simplelog.h"
#include "jiontctrllmgr.h"

#include <sphelper.h>
#include <spuihelp.h>

#pragma comment(lib,"sapi.lib")    //sapi.lib在SDK的lib目录,必需正确配置

SimpleTTS::SimpleTTS(QObject *parent)
	: QObject(parent), m_pVoice(NULL), m_pAudio(NULL)
{
	m_bTTSEnable = true;
	TCHAR szBuf[80] = { 0 };
	LPVOID lpMsgBuf = NULL;
	HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);

	if (FAILED(hr))
	{
		hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
		if (FAILED(hr))
		{
			FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |FORMAT_MESSAGE_FROM_SYSTEM,NULL,hr,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPTSTR)&lpMsgBuf,0, NULL);
			wsprintf(szBuf, _T("error %d: %s"), hr, lpMsgBuf);
			LocalFree(lpMsgBuf);
#ifdef QT_DEBUG
			qDebug().noquote() << "Error to intiliaze COM reason:" + QString::fromStdWString(szBuf);
#else
			LOGERROR("Error to intiliaze COM reason:"+ QString::fromStdWString(szBuf));
#endif // QT_DEBUG
			return;
		}
	}

	hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&m_pVoice);

	if (FAILED(hr))
	{
		FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL);
		wsprintf(szBuf, _T("error %d: %s"), hr, lpMsgBuf);
		LocalFree(lpMsgBuf);
#ifdef QT_DEBUG
		qDebug().noquote() << "Error to intiliaze ISPVoice component,reason: " + QString::fromStdWString(szBuf);
#else
		LOGERROR("Error to intiliaze ISPVoice component,reason: " + QString::fromStdWString(szBuf));
#endif // QT_DEBUG
		m_bTTSEnable = false;
		return;
	}

	hr = SpCreateDefaultObjectFromCategoryId(SPCAT_AUDIOOUT, &m_pAudio);
	if (FAILED(hr))
	{
		FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL);
		wsprintf(szBuf, _T("error %d: %s"), hr, lpMsgBuf);
		LocalFree(lpMsgBuf);
#ifdef QT_DEBUG
		qDebug().noquote() << "SpCreateDefaultObjectFromCategoryId failed,reason:" + QString::fromStdWString(szBuf);
#else
		LOGERROR("SpCreateDefaultObjectFromCategoryId failed,reason:" + QString::fromStdWString(szBuf));
#endif // QT_DEBUG
		m_bTTSEnable = false;
		return;
	}

	initVoices();

	WCHAR* szDesc;

	QStringList list;
	for (size_t i = 0; i < m_voices.size(); i++)
	{
		SpGetDescription(m_voices[i], &szDesc); //获取语音库描述
	
		list << QString::fromStdWString(std::wstring(szDesc));
		qDebug() << szDesc;
	}
	if (list.empty())LOGERROR(QString::fromLocal8Bit("TTS 没有可用的语音库"));
	else LOGINFO(QString::fromLocal8Bit("TTS 支持语音库:%1").arg(list.join(",")));

	int index = JiontCtrllMgr::getInstance()->config()->voiceIndex();
	if (index > 0 && index < m_voices.size())
	{
		hr = m_pVoice->SetVoice(m_voices[index]);
	}
	else
	{
#ifdef QT_DEBUG
		qDebug().noquote() <<QString::fromLocal8Bit("SetVoice index 无效,语音为正确设置!");
#else
		LOGERROR(QString::fromLocal8Bit("SetVoice index 无效,语音为正确设置!"));
#endif // QT_DEBUG
	}

	initSpFormat();
	//输出音频设置
	CSpStreamFormat Fmt;
	index = JiontCtrllMgr::getInstance()->config()->audioIndex();
	if (index >= 0 && index < m_spFmts.size())
	{
		Fmt.AssignFormat(m_spFmts[index].getFormat());
		hr = m_pAudio->SetFormat(Fmt.FormatId(), Fmt.WaveFormatExPtr());
		if (FAILED(hr))
		{
#ifdef QT_DEBUG
			qDebug().noquote() << QString::fromLocal8Bit("SetFormat TTS 初始化失败,TTS 不可用!");
#else
			LOGERROR(QString::fromLocal8Bit("SetFormat TTS 初始化失败,TTS 不可用!"));
#endif // QT_DEBUG
			m_bTTSEnable = false;
			return;

		}

		hr = m_pVoice->SetOutput(m_pAudio, FALSE);
		if (FAILED(hr))
		{
#ifdef QT_DEBUG
			qDebug().noquote() << QString::fromLocal8Bit("SetOutput TTS 初始化失败,TTS 不可用!");
#else
			LOGERROR(QString::fromLocal8Bit("SetOutput TTS 初始化失败,TTS 不可用!"));
#endif // QT_DEBUG
			m_bTTSEnable = false;
			return;
		}
	}
}

SimpleTTS::~SimpleTTS()
{
	m_pVoice->Release();
	m_pVoice = NULL;
	m_pAudio->Release();
	CoUninitialize();
}



void SimpleTTS::speak(QString text)
{
	if (!m_bTTSEnable)
	{
		printf("tts library cant use!");
		return;
	}
	for (int i = 0; i <= 9;i++)
	{
		QString temp = QString("%1").arg(i);
		text.replace(temp, "[" + temp + "]");
	}
	const QList<QPair<QString, QString>>& list = JiontCtrllMgr::getInstance()->config()->replaceList();
	for (int i = 0; i < list.size();i++)
	{
		text.replace(list[i].first, list[i].second);
	}
	
	m_pVoice->Speak((LPCTSTR)text.toStdWString().c_str(), SPF_ASYNC, NULL);
	//m_pVoice->WaitUntilDone(INFINITE);
}



SPSTREAMFORMAT SimpleTTS::currFormat()
{
	SPSTREAMFORMAT fmt = SPSF_Default;
	CComPtr<ISpStreamFormat> cpStream;
	HRESULT hr = m_pVoice->GetOutputStream(&cpStream);

	CSpStreamFormat Fmt;
	if (hr == S_OK)
	{
		hr = Fmt.AssignFormat(cpStream);
		if (SUCCEEDED(hr))
		{
			fmt = Fmt.ComputeFormatEnum();
		}
	}
	return fmt;
}

void SimpleTTS::initVoices()
{
	IEnumSpObjectTokens* cpEnum;
	HRESULT hr = SpEnumTokens(SPCAT_VOICES, NULL, NULL, &cpEnum);
	ULONG i = 0, ulCount = 0;
	hr = cpEnum->GetCount(&ulCount);

	ISpObjectToken* tok;
	while (SUCCEEDED(hr) && i < ulCount)
	{
		hr = cpEnum->Next(1, &tok, NULL);
		m_voices.push_back(tok);
		i++;
	}
	cpEnum->Release();
}

void SimpleTTS::initSpFormat()
{
	m_spFmts.push_back(SpFormat(SPSF_12kHz16BitStereo, "SPSF_12kHz16BitStereo"));
	m_spFmts.push_back(SpFormat(SPSF_16kHz16BitMono, "SPSF_16kHz16BitMono"));
	m_spFmts.push_back(SpFormat(SPSF_16kHz16BitStereo, "SPSF_16kHz16BitStereo"));
	m_spFmts.push_back(SpFormat(SPSF_22kHz16BitMono, "SPSF_22kHz16BitMono"));
	m_spFmts.push_back(SpFormat(SPSF_22kHz16BitStereo, "SPSF_22kHz16BitStereo"));
	m_spFmts.push_back(SpFormat(SPSF_24kHz16BitStereo, "SPSF_24kHz16BitStereo"));
	m_spFmts.push_back(SpFormat(SPSF_32kHz16BitStereo, "SPSF_32kHz16BitStereo"));
	m_spFmts.push_back(SpFormat(SPSF_44kHz16BitMono, "SPSF_44kHz16BitMono"));
	m_spFmts.push_back(SpFormat(SPSF_44kHz16BitStereo, "SPSF_44kHz16BitStereo"));
	m_spFmts.push_back(SpFormat(SPSF_48kHz16BitMono, "SPSF_48kHz16BitMono"));
	m_spFmts.push_back(SpFormat(SPSF_48kHz16BitStereo, "SPSF_48kHz16BitStereo"));
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
SpFormat::SpFormat(SPSTREAMFORMAT vl, QString  sz)
{
	m_val = vl;
	m_discription = sz;
	QString sChannel = m_discription.left(15);
	DWORD sampleRate, bitRate;
	sscanf(sz.toStdString().c_str(), "SPSF_%ukHz%uBit", &sampleRate, &bitRate);
	if (sampleRate == 22)
		sampleRate = 22050;
	else if (sampleRate == 44)
		sampleRate = 44100;
	else
		sampleRate *= 1000;
	m_bytePS = sampleRate*bitRate / 8;
	if (sChannel == "Stereo")
		m_bytePS *= 2;
}








微软TTS语音引擎编程入门

我们都使用过一些某某词霸的英语学习工具软件,它们大多都有朗读的功能,其实这就是利用的Windows的TTS(Text To Speech)语音引擎。它包含在Windows Speech SDK开发...
  • u012250337
  • u012250337
  • 2016年06月20日 17:18
  • 4793

TTS-零基础入门-10分钟教你做一个语音功能

在本片博客正式开始之前,大家先跟我做一个简单的好玩的 小语音。 新建一个文本文档,然后再文档里输入这样 一句话   CreateObject("SAPI.SpVoice").Speak "I Lov...
  • u010176014
  • u010176014
  • 2015年08月07日 00:35
  • 3373

Java TTS 语音开发

首先,本人声明本人只懂java,所以不知道具体怎么调用window的语音系统。用C、C++应该都可以,由于项目太急了,具体没去研究。     如果项目中只要读取英文,那倒直接可以用free tts,...
  • ykhappiness
  • ykhappiness
  • 2014年05月02日 22:45
  • 4254

封装微软TTS实现web系统文本转语音

将微软TTS语音引擎封装为Activex,实现web系统文本转语音,使用IE浏览器访问时实现点名及一些需要将文本转换成语音的需求。        插件封装后预留设置、文本转语音、播放进度等接口给页面j...
  • nxiaoping
  • nxiaoping
  • 2013年11月18日 19:49
  • 3378

使用C#开发TTS应用

使用C#开发TTS功能与NeoSpeech使用问题
  • qq354960984
  • qq354960984
  • 2016年09月28日 10:04
  • 1790

【VC++技术杂谈004】使用微软TTS语音引擎实现文本朗读

本文主要介绍如何使用微软TTS语音引擎实现文本朗读,以及生成wav格式的声音文件。   1.语音引擎及语音库的安装   TTS(Text-To-Speech)是指文本语音的简称,即通...
  • shaofei63
  • shaofei63
  • 2015年01月13日 20:56
  • 777

TTS-零基础入门之停止列表中单条语音播报

做了一个语音 循环播报列表信息。当我新删除了一天列表之后,发现它只有在下一轮播报中才会取消。这明显是不合理的。 一开始的代码是这样写的。 /// /// ...
  • u010176014
  • u010176014
  • 2015年08月18日 10:05
  • 1766

基于TextToSpeech(tts)引擎的朗读demo,很好玩的一个功能

很好玩的一个功能,有需要的朋友可以look look~~
  • DaJian35
  • DaJian35
  • 2015年01月09日 16:13
  • 1157

Android中文TTS语音合成

Android系统提供了5中语言的语音合成German,English,Spanish,French,Italian支持,其中不包括对中文的支持,但是Android系统允许第三方提供中文的TTS支持,...
  • niuls
  • niuls
  • 2014年04月15日 09:55
  • 2042

3个开源TTS(一)—安装使用

项目在身,要求分析几款开源的TTS引擎(不需要是中文的),然后选择一个为系统朗读英文文本提供接口。先从eGuideDog的余音(Ekho)开始,Ekho(余音)是一个把文字转换成声音的软件。它目前支持...
  • wangshuxuncom
  • wangshuxuncom
  • 2014年03月31日 09:45
  • 5316
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:微软TTS 使用
举报原因:
原因补充:

(最多只允许输入30个字)