本文主要介绍如何使用微软TTS语音引擎实现文本朗读,以及生成wav格式的声音文件。

1.语音引擎及语音库的安装

  TTS(Text-To-Speech)是指文本语音的简称,即通过TTS引擎把文本转化为语音输出。

  微软TTS语音引擎提供了Windows Speech SDK开发包供编程者使用。Windows Speech SDK包含语音合成SS引擎和语音识别SR引擎两种,语音合成引擎用于将文字转换成语音输出,语音识别引擎用于识别语音命令。

  Windows Speech SDK可以在微软的官网上免费下载,下载地址为:http://www.microsoft.com/download/en/details.aspx?id=10121

  在该下载界面中,选择下载SpeechSDK51.exe、SpeechSDK51LangPach.exe和sapi.chm 即可。其中,SpeechSDK51.exe是简体中文语音引擎,SpeechSDK51LangPach.exe是中文男生语音库,sapi.chm是SAPI(The Microsoft Speech API)帮助文档。

  下载完成后,先安装语音引擎SpeechSDK51.exe,再安装中文语音库SpeechSDK51LangPach.exe。安装完成后,可以依次点击【开始】/【控制面板】/【语言】打开图1所示的语言属性对话框。在该对话框的“文字-语音转换”标签页下的“语音选择”中能够看到当前系统安装的全部可用的语音库。

图1 语言属性对话框

2.ISpVoice接口的成员函数

  文本朗读的功能主要是通过使用ISpVoice接口的成员函数来实现的。该接口的常用成员函数有如下一些:

(1)HRESULT Speak(LPCWSTR *pwcs, DWORD dwFlags, ULONG *pulStreamNumber);  //朗读文本

(2)HRESULT Pause ( void);                                   //暂停朗读

(3)HRESULT Resume ( void);                                //恢复朗读

(4)HRESULT SetRate( long RateAdjust);                 //设置朗读速度(取值范围:-10到10)

(5)HRESULT GetRate(long *pRateAdjust);              //获取朗读速度

(6)HRESULT SetVoice(ISpObjectToken   *pToken);      //设置使用的语音库

(7)HRESULT GetVoice(ISpObjectToken** ppToken);    //获取语音库

(8)HRESULT SetVolume(USHORT usVolume);          //设置音量(取值范围:0到100)

(9)HRESULT GetVolume(USHORT *pusVolume);      //获取音量

(10)HRESULT SetOutput(IUnknown *pUnkOutput,BOOL fAllowFormatChanges);     //设置输出

(11)HRESULT SpeakStream(IStream *pStream, DWORD dwFlags, ULONG *pulStreamNumber);   //朗读wav数据流

3.编程实例

  了解了以上一些ISpVoice接口的成员函数之后,我们就可以开始编写程序来实现文本朗读,以及生成wav格式声音文件的功能了。

3.1环境配置

  首先,我们需要将Windows Speech SDK开发包的头文件和库文件所在路径添加到编译器中,具体方法如下(这里以VC++6.0为例):

  依次点击【工具】/【选项】,打开选项对话框,选择【目录】标签,在【路径】中加入“C:\Program Files\Microsoft Speech SDK 5.1\Include”和“C:\Program Files\Microsoft Speech SDK 5.1\Lib\i386”。如图2所示。

图2 选项对话框

  其次,还需要在工程中包含TTS语音引擎头文件和库文件,具体如下:

 #include <sapi.h>                            //包含TTS语音引擎头文件和库文件
#include <sphelper.h>
#pragma comment(lib, "sapi.lib")

3.2枚举语音库

  枚举语音库需要使用到SpEnumTokens()函数,该函数原型如下:

 inline HRESULT SpEnumTokens(
const WCHAR *pszCategoryId,
const WCHAR *pszReqAttribs,
const WCHAR *pszOptAttribs,
IEnumSpObjectTokens **ppEnum
);

  其中,参数ppEnum是IEnumSpObjectTokens类型的指针,用于存储枚举得到的所有语音Token。IEnumSpObjectTokens的成员函数GetCount()用于得到语音Token的总个数,而成员函数Item()则用于得到具体的某一个语音Token。

  如下的代码示例如何枚举得到的所有语音Token,并将得到的语音库的名字添加到下拉组合框控件中,具体实现如下:

 /*
* 函数功能 : 初始化语言包选择组合框控件
* 备 注 :
* 作 者 : 博客园 依旧淡然
*/
void CTTSDemoDlg::InitVoicePackageSelComboxCtrl()
{
//初始化COM组件
if(FAILED(::CoInitialize(NULL)))
{
MessageBox("初始化COM组件失败!", "提示", MB_OK|MB_ICONWARNING);
return;
} //枚举所有语音Token
if(SUCCEEDED(SpEnumTokens(SPCAT_VOICES, NULL, NULL, &m_pIEnumSpObjectTokens)))
{
//得到所有语音Token的个数
ULONG ulTokensNumber = ;
m_pIEnumSpObjectTokens->GetCount(&ulTokensNumber); //检测该机器是否安装有语音包
if(ulTokensNumber == )
{
MessageBox("该机器没有安装语音包!", "提示", MB_OK|MB_ICONWARNING);
return;
} //将语音包的名字加入组合框控件
CString strVoicePackageName = _T("");
CString strTokenPrefixText = _T("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Speech\\Voices\\Tokens\\");
for(ULONG i=; i<ulTokensNumber; i++)
{
m_pIEnumSpObjectTokens->Item(i, &m_pISpObjectToken);
WCHAR* pChar;
m_pISpObjectToken->GetId(&pChar);
strVoicePackageName = pChar;
strVoicePackageName.Delete(, strTokenPrefixText.GetLength());
m_ComboxVoiceSel.InsertString(i, strVoicePackageName);
} //设置默认的语音包选择
m_ComboxVoiceSel.SetCurSel();
}
}

  通过以上的代码可以看到,首先,我们通过调用CoInitialize()函数完成了对COM组件的初始化。然后,我们调用SpEnumTokens()函数得到了m_pIEnumSpObjectTokens对象,该对象存储了枚举得到的所有语音Token。紧接着,我们调用GetCount()函数得到个数,并调用Item()函数得到具体的每一个语音Token对象m_pISpObjectToken。最后,我们通过调用m_pISpObjectToken对象的GetId()函数便能得到具体的某一个Token对象的ID,其形式为“HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\XXXXX”,去掉其前缀便能得到具体的语音库的名字了。

  该实例运行效果如图3所示,点击“语音包选择”组合框下拉箭头,能够看到与图1中列出的语音包是一致的。

图3 TTS示例运行效果

3.3文本朗读

  点击图3所示界面中的“开始朗读”按钮,能够根据当前所选择的语音包以及设定的语速和音量,对朗读内容编辑框中的内容进行朗读。其具体实现方法如下:

 /*
* 函数功能 : 点击"开始朗读"按钮时,该函数被调用
* 备 注 :
* 作 者 : 博客园 依旧淡然
*/
void CTTSDemoDlg::OnButtonStartRead()
{
UpdateData(true); //获取ISpVoice接口
if(FAILED(CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_INPROC_SERVER, IID_ISpVoice, (void**)&m_pISpVoice)))
{
MessageBox("获取ISpVoice接口失败!", "提示", MB_OK|MB_ICONWARNING);
return;
} //设置语言
m_pIEnumSpObjectTokens->Item(m_ComboxVoiceSel.GetCurSel(), &m_pISpObjectToken);
m_pISpVoice->SetVoice(m_pISpObjectToken); //设置播放速度
m_pISpVoice->SetRate(m_SliderVoiceSpeed.GetPos() - ); //设置音量大小
m_pISpVoice->SetVolume( - m_SliderVoiceSize.GetPos()); //检测朗读内容是否为空
if(m_EditContent.IsEmpty())
{
MessageBox("朗读内容不能为空!", "提示", MB_OK|MB_ICONWARNING);
return;
} //开始进行朗读
m_pISpVoice->Speak(m_EditContent.AllocSysString(), SPF_ASYNC, NULL);
}

  在以上代码中可以看到,使用了ISpVoice接口函数来完成语音库的选择、语速和音量大小的设定,以及通过调用Speak()函数进行文本朗读。

3.4生成WAV格式的声音文件

  要将文本朗读的声音保存为WAV格式的声音文件,主要是通过调用ISpVoice接口函数GetOutputStream()和SetOutput()来实现的。

以下的代码段给出了实现该功能的示例:

     //生成WAV文件
CComPtr<ISpStream> cpISpStream;
CComPtr<ISpStreamFormat> cpISpStreamFormat;
CSpStreamFormat spStreamFormat;
m_pISpVoice->GetOutputStream(&cpISpStreamFormat);
spStreamFormat.AssignFormat(cpISpStreamFormat);
HRESULT hResult = SPBindToFile("C:\\Documents and Settings\\Administrator\\桌面\\TEST\\test.wav",
SPFM_CREATE_ALWAYS,
&cpISpStream,
&spStreamFormat.FormatId(),
spStreamFormat.WaveFormatExPtr());
if(SUCCEEDED(hResult))
{
m_pISpVoice->SetOutput(cpISpStream, TRUE);
m_pISpVoice->Speak(m_EditContent.AllocSysString(), SPF_DEFAULT, NULL);
MessageBox("生成WAV文件成功!", "提示", MB_OK);
}
else
{
MessageBox("生成WAV文件失败!", "提示", MB_OK|MB_ICONWARNING);
}

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

  1. 微软TTS语音引擎编程入门

    原文链接地址:http://www.jizhuomi.com/software/135.html   我们都使用过一些某某词霸的英语学习工具软件,它们大多都有朗读的功能,其实这就是利用的Windows ...

  2. 【VC++技术杂谈003】打印技术之打印机状态监控

    在上一篇博文中我主要介绍了如何获取以及设置系统的默认打印机,本文将介绍如何对打印机状态进行实时监控,记录下所打印的文档.打印的份数以及打印时间等打印信息. 1.打印机虚脱机技术 在正式介绍如何对打印机 ...

  3. 【VC++技术杂谈001】音频技术之调节音量及设置静音

    本文主要介绍如何使用混音器Mixer API函数实现系统音量调节,以及设置静音. 1.混音器的作用及结构 1.1混音器的作用 声卡(音频卡)是计算机进行声音处理的适配器,具有三个基本功能: (1)音乐 ...

  4. 【VC++技术杂谈008】使用zlib解压zip压缩文件

    最近因为项目的需要,要对zip压缩文件进行批量解压.在网上查阅了相关的资料后,最终使用zlib开源库实现了该功能.本文将对zlib开源库进行简单介绍,并给出一个使用zlib开源库对zip压缩文件进行解 ...

  5. 【VC++技术杂谈007】使用GDI+进行图片格式转换

    本文主要介绍如何使用GDI+对图片进行格式转换,可以转换的图片格式为bmp.jpg.png. 1.加载GDI+库 GDI+是GDI图形库的一个增强版本,提供了一系列Visual C++ API.为了使 ...

  6. 【VC++技术杂谈006】截取电脑桌面并将其保存为bmp图片

    本文主要介绍如何截取电脑桌面并将其保存为bmp图片. 1. Bmp图像文件组成 Bmp是Windows操作系统中的标准图像文件格式. Bmp图像文件由四部分组成: (1)位图头文件数据结构,包含Bmp ...

  7. 【VC++技术杂谈005】如何与程控仪器通过GPIB接口进行通信

    在工控测试系统中,经常需要使用到各类程控仪器,这些程控仪器通常具有GPIB.LAN.USB等硬件接口,计算机通过这些接口能够与其通信,从而实现自动测量.数据采集.数据分析和数据处理等操作.本文主要介绍 ...

  8. 【VC++技术杂谈002】打印技术之获取及设置系统默认打印机

    本文主要介绍如何获取以及设置系统的默认打印机. 1.获取系统中的所有打印机 获取系统中的所有打印机可以使用EnumPrinters()函数,该函数可以枚举全部的本地.网络打印机信息.其函数原型为: B ...

  9. 微软TTS尝试系列之开篇杂谈(仅思路)

    第一次写博客,不知道如何下手,思路也乱,就先聊聊怎么进的园子吧,但愿不会浪费大家太多的宝贵时间>_<. 与博客园结缘应该是大三刚开始的时候.当时学校教务处想开发一个教务安排系统,为了省钱就 ...

随机推荐

  1. gcc编译器用法(自学总结)

    GCC仅仅意味着GNU C Compiler. gcc工作四个步骤:预处理,编译,汇编,连接. 1.预处理: 编译器将C源代码中的包含的头文件如stdio.h编译进来,用户可以使用gcc的选项&quo ...

  2. mysql的查询、子查询及连接查询

    >>>>>>>>>> 一.mysql查询的五种子句         where(条件查询).having(筛选).group by(分组). ...

  3. BZOJ 4066 简单题 ——KD-Tree套替罪羊树

    [题目分析] 直接x,y二维轮番划分,暴力即可. 套上替罪羊,打碎重构,对于时间复杂度有了保证. 写起来好麻烦,重构的技巧很棒! [代码] #include <cstdio> #inclu ...

  4. css定位学习经验记录

    之前了解到css的定位position属性,常用的三种: position:absolute 1.当父元素定位为relative时,以父元素为起始坐标定位. 2.当父元素没有定位时,以body为起始坐 ...

  5. docker 1.8+之后ubuntu安装指定版本docker-engine

    这边记录ubuntu安装过程,首先是官网文档 If you haven’t already done so, log into your Ubuntu instance. Open a termina ...

  6. ehcache注解全面解析---打酱油的日子

    通过ehcache以编程方式使用缓存: 跟上面的方式相同,但是缓存通过ehcache去管理,当然比使用map有N多种好处,比如缓存太大了快达到上限之后,将哪一部分缓存清除出去.这种方式完全是通过代码的 ...

  7. webdav不识别软链接?解决办法?

    webdav不识别软链接?解决办法? 在使用webdav实现公网存储共享的时候,发现webdav并不支持软链接的共享,如下源代码可以100%确定这个问题 /* ### for now, only pr ...

  8. 迎战Meta 2,微软新专利有望解决Hololens视场角野窄问题

    上周,微软HoloLens的竞争对手AR眼镜Meta 2正式发货,微软是该急了.我们知道Meta 2不仅在价格上比HoloLens便宜,而且在性能上也不弱,Meta2的可视角度达到90度,比HoloL ...

  9. 2分钟 sublime设置自动行尾添加分号并换行:

    18:03 2016/4/162分钟 sublime设置自动行尾添加分号并换行:注意:宏文件路径要用反斜杠/,2个\\会提示无法打开宏文件.不需要绝对路径很简单利用宏定义:1.录制宏:由于是录制动作宏 ...

  10. nodejs redis 发布订阅机制封装

    最近项目使用redis,对publish 和 subscribe的使用进行了了解,并进行了封装. var config = require('../config/config'); var log = ...