Microsoft Speech SDK提供关于语音(Speech)处理的一套应用程序编程接口SAPI(Speech Application Programming Interface)。SAPI提供了实现文字-语音转换(Text-to-Speech)和语音识别(Speech Recognition)程序的基本函数,大大简化了语音编程的难度,降低了语音编程的工作量。Speech SDK可以免费从如下网址下载:http://www.microsoft.com/speech。
由于Speech SDK是以COM接口的方式提供服务的,所以首先介绍COM的有关基础知识。
Speech SDK提供了完善的COM接口,所以具备一定的COM编程基础对进行Speech SDK编程来说是非常必要的。笔者将简要介绍COM编程的基础知识。虽然这些知识对阅读本书来说是足够了,但是如果你没有进行过任何的COM编程实践,笔者还是建议你先阅读一本COM的教科书。
1.什么是COM
组件对象模型(Component Object Model,COM)对象是符合COM规范的可重用的软件组件。符合COM规范的COM对象相互之间可以很好地工作,并且可以很容易地集成到应用程序中。从应用的观点来看,一个COM对象就是一个黑箱,应用程序可以使用它来创建一项或多项任务。
COM对象常常用动态链接库(Dynamic Link Libraries,DLLs)的形式来实现。与传统的DLL一样,COM对象暴露其方法,应用程序能调用这些方法来实现对象所支持的功能。应用程序与COM对象的关系就像应用程序与C++对象的关系,但其中也存在一些区别。
1)COM对象执行严格的封装。你不能简单地创建一个对象就调用其中的公用方法。COM对象的公用方法聚合为一个或多个接口。为了使用一个方法,必须先创建一个对象,并从对象中获得相应的接口。一个接口一般包含一组方法,通过它们能使用对象的特定功能。不能通过接口来调用不属于该接口的方法。
2)创建COM对象的方法与创建C++对象的方法不同。有多种方法可以创建COM对象,但所有的方法都需要使用COM的细节技术。Speech SDK应用程序编程接口(API)包括许多的帮助函数和方法,它们简化了创建大部分Speech对象的工作。
3)必须使用COM的细节技术来控制对象的生命期。
4) COM对象不需要明确地装载。COM对象一般包含在DLL中。然而,与使用普通DLL中的方法不同,使用COM对象时,不需要明确地装载DLL或链接静态库。每一个COM对象都具有一个惟一的注册标识。用该标识来创建对象时,COM将自动地装载正确的DLL。
5)COM是一种二进制规范。COM对象可用许多种编程语言来编写和调用。对于使用者来说,并不需要了解对象的源程序的任何信息。比如,Microsoft Visual Basic编写的应用程序可以很好地调用C++编写的COM对象。
下面先介绍COM的几个基本概念,包括对象与接口、GUID、HRESULT类型和指针地址等。
(1)对象与接口
理解对象与接口的区别是非常重要的。通常用其主要接口的名称来称呼对象。但是,严格地说,这两个术语是不能互换使用的。
一个对象能暴露任何数量的接口。例如,所有的对象都必须暴露IUnknown接口,它们一般还暴露至少一个其他的接口,它们也可能暴露更多的接口。为了使用一个特定的方法,首先必须获得正确的接口指针。
多个不同的接口可以暴露同一个接口。一个接口就是一组执行特定操作的方法。接口定义只是指定了方法的调用语法和它们的一般功能。任何需要支持一组特定操作的COM对象都可通过暴露一个合适的接口来实现这些特定的操作。有些接口非常专业,仅仅由单一的对象来暴露。有些接口在许多场合下都非常有用,它们可由许多的对象来暴露。最极端的例子是IUnknown接口,所有的COM对象都需要暴露它。
如果一个对象暴露一个接口,它必须支持接口定义的每一个方法。但是,不同的对象实现一个特定方法的方式是不同的。不同的对象可能使用不同的算法来实现最后的结果。有时一个对象暴露一组通用的方法,但往往只需要支持其中的一部分方法。可是其他的未被支持的方法也需要能被成功调用,只是它们都只返回E_NOTIMPL。
COM标准要求接口一旦发布,其定义就不能再改变。例如,不能在一个已有的接口中增加一个新的方法,而必须创建一个新的接口。如果没有限制接口中必须有什么样的方法,通常的做法是在其下一代接口中包括原有接口的所有方法和其他的新方法。
但是一个接口有几代版本的情况并不常见。通常所有的版本实现完全相同的功能,只是在具体实现细节上不同。一个对象经常暴露所有版本的接口。这样做使较老版本的应用程序能继续使用对象的较老版本的接口,而较新版本的应用程序能使用较新版本接口的强大功能。一般来说,同一家族的接口具有相同的名称,在名称后加一个整数来表示不同的版本。比如,原来的接口叫做IMyInterface,接下来的两代版本就分别叫做IMyInterface2和IMyInterface3。
(2)GUID
全球惟一标识符(Globally Unique Identifiers,GUIDs)是COM编程模型的关键部分。从本质上来说, GUID是一个128位的结构。然而,GUID在创建时必须保证不能出现两个相同的GUID。COM在如下的两个方面广泛地使用GUID:
1)惟一地标识一个特定的COM对象。赋予一个COM对象的GUID叫做一个类标识(class ID,CLSID)。当需要创建一个相关的COM对象的实例时,需要使用CLSID。
2)惟一地标识一个特定的COM接口。与一个COM接口相关的GUID 叫做接口标识(interface ID,IID)。当从一个对象中请求一个特定接口时,需要使用IID。不管哪个对象暴露接口,该接口的IID都是相同的。
为了叙述方便,在文档中经常使用对象和接口的描述名称来引用对象和接口。虽然这样做并不会在文档中引起混乱,但是,严格地说,并不能保证不会有两个或多个不同的对象或接口具有相同的描述名。惟一的不能引起歧义的方法是用GUID 来引用对象或接口。
虽然GUID是一种结构,但却经常表示为对应的字符串。最常用的GUID字符串形式是“{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}”, 其中x 为一个十六进制整数。
由于实际的GUID很长,并且很容易写错,所以一般还提供一个等价的名称。在调用CoCreateInstance之类的函数时,可以使用这个名称而不使用实际的GUID结构。通常的命名惯例是分别在对象或接口的描述名称前加上CLSID_或IID_作为前缀。例如,ISpVoice接口的CLSID的名称是CLSID_ISpVoice。
(3)HRESULT类型值
所有的COM方法都返回一个32位的HRESULT类型的值。对大部分方法而言,HRESULT本质上是一个包含独立的两部分信息的结构:
1)方法调用成功了还是失败了;
2)关于方法所支持操作的结果的更详细信息。
有些方法仅仅返回在Winerror.h中定义的标准的HRESULT类型值。然而,方法可以返回自己定义的HRESULT类型值,以提供更专用的信息。当然这样的自定义值一般会在方法的参考文档中说明。需要注意的是,参考文档可能不会列出标准的HRESULT类型值,但方法却可能返回标准值。
虽然HRESULT类型值经常用来返回错误信息,但不要将它们看成错误码。由于说明成功或失败的位在包含详细信息的数据中是分别存储的,所以HRESULT类型值可以包含任何数量的成功和失败代码。作为一种惯例,成功码的名称具有S_前缀,错误码的名称具有E_前缀。比如,最常用的成功码和错误码分别是S_OK和E_FAIL。
由于COM方法可能返回许多不同的成功码或错误码,因此必须很小心地测试HRESULT类型的值。例如,假设一个方法在文档中说明成功,返回S_OK,不成功则返回E_FAIL。这时,请注意,该方法可能还会返回其他的成功码或错误码。下面的代码段说明了使用简单测试的危险。
// hr 是该方法返回的HRESULT类型值
if(hr == E_FAIL)
{
// 处理错误
}
else
{
// 处理成功
}
只有在该方法只返回E_FAIL 来指示失败时,上面的测试才能正常工作。然而,该方法还可能返回一个E_NOTIMPL或 E_INVALIDARG之类的错误值。上面的代码将会将它们解释为成功,这将可能导致程序的失败。
如果需要关于方法运行结果的更详细的信息,必须测试每一个相关的HRESULT值。但经常只关心方法是成功的还是失败的。一种可靠的测试HRESULT类型值说明成功还是失败的方法是利用如下的宏来判断,这些宏定义在Winerror.h中。
1)宏SUCCEEDED返回TRUE作为成功码,返回FALSE作为失败码;
2)宏FAILED返回TRUE作为失败码,返回FALSE作为成功码;
可以使用宏FAILED来修改上面的代码段:
// hr 是该方法返回的HRESULT类型值
if(FAILED(hr))
{
// 处理错误
}
else
{
// 处理成功
}
这段代码能合理地处理E_NOTIMPL和E_INVALIDARG之类的失败码。
大多数的COM方法返回结构化的HRESULT类型值,只有很少数量的方法使用HRESULT来返回简单的整数值,这类方法经常是成功的。如果将这类整数值传给宏SUCCESS,该宏将总是返回TRUE。常用的例子是IUnknown::Release方法,它减少一次对象的引用计数并返回当前的引用计数。将在管理对象的生命期一节中讨论引用计数的问题。
(4)指针地址
阅读一些COM方法的参考文档时,经常看到如下的说明:
HRESULT CreateDevice(
.
.
.
IDirect3DDevice8 **ppReturnedDeviceInterface
);
C或C++开发人员熟悉普通的指针,但是COM经常使用另外的间接层。这种间接的第二层用两个星号(**)跟着类型声明来表示。变量名一般使用“pp”作为前缀。在上面的例子中,参数ppReturnedDeviceInterface表示指向IDirect3DDevice8 接口的指针的地址。
与C++不同,不需要直接访问COM对象的方法,而必须获取指向方法的接口的指针。然后像调用指向C++方法的指针一样来调用方法。例如,使用如下的语法来调用方法IMyInterface::DoSomething method:
IMyInterface *pMyIface;
.
.
.
pMyIface->DoSomething(...);
这样做的原因是我们并不直接创建接口指针,而是必须调用不同的方法(例如上述的CreateDevice)来创建接口指针。为了使用这种方法来获取接口指针,应声明一个指向需要的接口的指针变量,并将该指针变量的地址,即一个指针的地址,传递给该方法。当该方法返回时,该变量将指向你要求的接口,可以使用该指针来调用接口的任何方法。将在使用COM接口一节中更详细地讨论接口指针的问题。
2.创建COM对象
有多种方法可以创建COM对象。以下是在编程中最常用的两种方法。
1)直接法:将对象的CLSID传给CoCreateInstance函数。该函数将创建对象的一个实例,并返回指向你所指定接口的指针。
2)间接法:调用一个特殊的方法或函数来创建对象。这类方法创建对象并返回该对象的接口。使用这种方式来创建对象时,通常并不能指定需要返回的接口。
创建对象之前,必须调用CoInitialize函数来初始化COM。如果使用间接法来创建对象,对象的创建方法将自动完成COM初始化。如果使用CoCreateInstance来创建对象,则必须明确地调用CoInitialize。当完成了所有的COM工作后,必须调用CoUninitialize 来卸载COM。如果调用了CoInitialize,则必须对应地调用一次CoUninitialize。一般地说,需要明确,初始化COM的应用程序在其启动过程中初始化COM,在其清除过程中卸载COM。
用CoCreateInstance 来创建一个COM对象的实例需要使用该对象的CLSID。如果其CLSID是公开的,可以在其参考手册或相应的头文件中找到。如果其CLSID不是公开的,则不能使用直接法来创建该对象。
CoCreateInstance函数有5个参数,一般可以按如下方式来设置其参数。
1)rclsid:将该参数设为需要创建的对象的CLSID。
2)pUnkOuter:将该参数设为NULL。只有在聚合对象时才需要使用该参数。参见关于聚合的讨论。
3)dwClsContext:将该参数设为CLSCTX_INPROC_SERVER。该值说明对象是在DLL中实现的,将作为你的应用程序进程的一部分来运行。
4)riid:将该参数设为需要返回的接口的IID。该函数将创建指定的对象,并通过参数ppv返回所请求的接口指针。
5)ppv:将该参数设为riid所指定的接口的指针地址。该变量应该声明为一个指向请求的接口的指针。在参数表中,该参数应被强制为(LPVOID *)类型。
例如,下面的代码段创建了ISpVoice对象的一个新的实例,函数返回时,m_IpVoice 变量是指向ISpVoice接口的指针。如果发生错误,程序将终止,并显示一个消息框。
CComPtr<ISpVoice> m_IpVoice = NULL;
HRESULT hr;
hr = m_IpVoice.CoCreateInstance(CLSID_SpVoice);
if (FAILED(hr))
{
AfxMessageBox("Error creating voice");
return FALSE;
}
用间接法创建对象往往简单得多。只要将接口指针的地址传给对象的创建方法,该方法就会创建对象并返回接口指针。但间接地创建对象时,一般不能选择返回哪个接口。但是可以指定如何来创建对象。
3.IUnknown接口
所有的COM对象都支持一个叫做IUnknown的接口。该接口提供了对对象的生命期的控制和检索对象的其他接口的方法。IUnknown接口有以下3个方法。
1)AddRef:当一个接口或另一个应用程序与对象绑定时,对对象的引用计数加1。
2)QueryInterface:查询对象所支持的功能,并请求指向指定的接口的指针。
3)Release:对对象的引用计数减1。当引用计数变为0时,对象将被释放。
AddRef和Release方法维护对象的引用计数。例如,当创建一个对象时,该对象的引用计数变为1。每次一个函数返回一个指向该对象的接口的指针时,该函数都必须调用AddRef来增加其引用计数。AddRef的每一次调用都必须与Release的调用相匹配。在指针被释放前必须对该指针调用Release。当一个对象的引用计数变为0时,该对象将被释放,它的所有接口都将变为无效接口。
QueryInterface方法用来确定一个对象是否支持指定的接口。如果一个对象支持一个接口,QueryInterface返回一个指向该接口的指针。然后就可以使用该接口的方法来与对象进行通信。如果QueryInterface成功地返回一个指向接口的指针,它将明确地调用AddRef来增加引用计数。因此在释放该接口的指针之前,应用程序必须调用Release来减少引用计数。
4.使用COM接口
获得接口指针后,可以用该指针来访问接口的任何方法。
在许多情形中,从创建方法接收到的接口指针就是所需要的。实际上,只暴露一个除IUnknown之外的接口的对象是很不常见的。相反,许多的对象暴露多个接口,需要指向这些接口的多个指针。如果需要创建方法所返回的接口之外的更多的接口,则并不需要再创建一个新的对象。可以使用对象的IUnknown::QueryInterface 方法来请求其他接口的指针。
如果使用CoCreateInstance来创建对象,则可以请求一个IUnknown接口指针,然后调用IUnknown::QueryInterface方法来请求需要的每一个接口。然而,当只需要一个接口时,这种方法显得很不方便。而且,如果使用不允许指定哪个接口应该返回的创建方法时,这种方法更不能工作。在实践中,经常不需要获得一个明确的IUnknown指针,因为所有的COM接口都是从IUnknown接口继承或扩展而来的。
扩展一个接口很像继承一个C++类。子接口暴露父接口的所有方法,并增加一个或多个自己的方法。事实上,经常看见“继承”而不是“扩展”。需要记住的是,继承只能出现在对象的内部。应用程序不能继承或扩展另一个对象的接口,但是可以使用子接口来调用它自己或其父接口的方法。
由于所有接口都是IUnknown的子接口,因此可以使用已经获得的任何该对象接口的指针来调用QueryInterface。这样做时,需要提供你请求的接口的IID和当方法返回时存放接口指针的指针。
5.管理COM对象的生命期
当对象被创建时,系统将分配必需的内存资源。当一个对象不再需要时,应该删除它。系统将收回它所占有的内存,以用于其他目的。对于C++对象,应直接使用new 和delete 操作符来控制对象的生命期。COM不允许直接创建或删除对象。其原因是同一对象可能被多个应用程序所使用。如果其中的一个应用程序要删除该对象,其他的应用程序就可能失败。实际上,COM采用引用计数系统来控制对象的生命期。
对象的引用计数就是其中的接口被请求的次数。接口每被请求一次,其引用计数都将增加。当不再需要接口时,应用程序将释放该接口,并减少其引用计数。只要引用计数大于0,对象将保留在内存中。当引用计数变为0时,对象将释放自己。我们不必关心对象的引用计数,只需要正确地获取和释放对象的接口,对象将具有适当的生命期。
合理地处理引用计数对COM编程来说是非常重要的。处理不当将导致内存泄漏。COM编程人员最常见的错误是不释放接口。当出现这样的错误时,引用计数将永远不能变为0,对象将不确定地保留在内存中。
只要获得一个新的接口指针,引用计数就必须调用IUnknown::AddRef 来增加。但是,应用程序通常不需要调用该方法。如果通过调用一个对象创建方法或IUnknown::QueryInterface来获得接口指针,对象将自动地增加引用计数。如果用其他的方法来创建接口指针,比如拷贝已有的指针,就必须明确地调用IUnknown::AddRef。否则,当释放原来的接口指针时,你得到的对象可能被破坏,即使你还需要使用该指针的拷贝。
不论明确地或对象自动地增加了引用计数,都必须释放接口指针。当不再需要接口指针时,调用IUnknown::Release来减少引用计数。一种常用的方法是,将所有的接口指针初始化为NULL,并在释放接口后将它们重新设为NULL。这样可以在清除代码中测试所有的接口指针。那些不为NULL的接口指针就是仍然活动的,需要在退出应用程序之前释放它们。
6.用C来操作COM对象
虽然C++是最常用的COM编程语言,有些时候也需要使用C语言来访问COM对象。这样做更加直截了当,但也要用到更加复杂的语法。
所有的方法都需要一个额外的参数,放在参数表的最前面。这个参数必须设为接口指针。而且,必须明确地引用对象的vtable。
每个COM对象都有一个包含指向对象所暴露的方法的指针的列表。接口指针指向vtable 的相应位置,该位置包含了指向你要调用的特定方法的指针。使用C++时,不必关心vtable,因为它在C++中是不可见的。当然,如果需要使用C来调用COM方法,就必须包括一个明确引用vtable的间接的额外层。
有些组件在它们的头文件中定义了一些宏,它们能很好地解决如何正确地调用COM。
7.用ATL来处理COM接口
如果使用活动模板库(Active Template Library,ATL)来处理Microsoft Speech,为了兼容ATL,必须首先重新声明接口。这将允许你合理地使用CComQIPtr类来获取指向接口的指针。
如果没有为ATL重新声明接口,则会得到下面的错误信息:
C:/Program Files/Microsoft Visual Studio/VC98/ATL/INCLUDE/atlbase.h(566) :
error C2787: 'ISpVoice' : no GUID has been associated with this object
11.1.2 SAPI接口
SAPI提供应用程序和语音引擎之间的高层接口,它实现并隐藏了控制和管理不同语音引擎的实时操作的底层技术细节。SAPI的结构如图11-1所示。
最基本的语音引擎包括Text-To-Speech(TTS)和语音识别器。 TTS 通过合成声音来朗读文本字符串和文本文件。语音识别器将人类的语音转换成可阅读的字符串和文件。
1.Text-To-Speech API
应用程序通过ISpVoice 组件对象接口(Component Object Model Interface)来控制Text-To-Speech (TTS)。一旦应用程序创建了IspVoice对象,调用接口IspVoice的方法ISpVoice::Speak,就能产生朗读指定的文字的声音。IspVoice接口还提供了其他一系列的方法来改变声音和其合成特征,比如控制语速的ISpVoice::SetRate,控制输出音量的ISpVoice::SetVolume和改变当前语音的ISpVoice::SetVoice。
在输入用于朗读的文字中还可插入一系列的特殊SAPI控制符,用来控制输出声音的实时合成特性,如语音、语调、重音、语速和音量等。合成标志文件sapi.xsd用来说明声音的合成特性。sapi.xsd是一种标准XML格式文件,它与特定的引擎或当前正在使用的语音无关,是一种简单而功能强大的定制TTS语音的方法。
IspVoice::Speak方法既能同步地(在语音播放完之后才返回)也能异步地(语音开始播放就返回,语音播放在后台处理)操作语音。 指定SPF_ASYNC 作为播放方式时,语音异步播放。这时可调用ISpVoice::GetStatus方法来获取实时状态信息,如播放状态、当前播放的文字位置等。同时,既可以打断当前的播放而立即播放新的文字(需指定SPF_PURGEBEFORESPEAK参数),也可以在播放完当前文字之后再自动播放新的文字。
除了IspVoice接口以外, SAPI还提供了许多有用的COM接口来实现高级的TTS应用程序。
(1)事件(Events)
SAPI通过使用标准的回调机制(窗口消息、回调函数或Win32事件)来与应用程序传送事件。对于TTS,事件主要用来同步语音输出。应用程序能同步处理语音输出和实时动作,比如词语分界、音位或嘴形动画分界及应用程序定制的分界等。应用程序可通过调用IspNotifySource,IspNotifySink,IspNotifyTranslator,IspEventSink,IspEventSource和ISpNotifyCallbackcan来初始化和处理这些实时事件。
(2)词典(Lexicons)
通过调用IspContainerLexicon,IspLexicon和IspPhoneConverter接口提供的方法,应用程序能为语音合成引擎设置定制的词汇发音。
(3)资源(Resources)
下面的COM接口用于处理SAPI语音数据(比如声音文件和发音词典):
IspDataKey,IspRegDataKey,IspObjectTokenInit,IspObjectTokenCategory,IspObjectToken,IenumSpObjectTokens,IspObjectWithToken,IspResourceManager和IspTask。
(4)声音(Audio)
SAPI还提供了定制声音输出到特定目标(如电话和客户硬件)的接口,包括IspAudio,ISpMMSysAudio,IspStream,IspStreamFormat和IspStreamFormatConverter。
2.语音识别API
正如IspVoice是主要的语音合成接口一样,IspRecoContext是语音识别的主要接口。与IspVoice一样,它也是一种ISpEventSource接口,提供了为请求的语音识别事件接收通知消息的基本载体。
有两种不同的语音识别引擎(ISpRecognizer),即共享语音识别引擎(shared speech recognition engine)和进程内语音识别引擎(InProc speech recognition engine)。应用程序可以选择其中的一种。
一般推荐使用共享语音识别引擎,这种引擎能被多个应用程序共享。创建共享IspRecognizer的IspRecoContext接口很简单,应用程序只需指定参数为组件的CLSID_SpSharedRecoContext并调用COM的CoCreateInstance函数即可。这时,SAPI将设置音频输入流为SAPI的默认音频输入流。
对于单独运行于一个系统中的大型服务器应用程序,其运行效率是很重要的。这时使用进程内语音识别引擎更合适。使用进程内语音识别引擎有3个步骤:首先,应用程序需指定参数为组件的CLSID_ SpInprocRecoInstance并调用COM的CoCreateInstance函数来创建其自己的进程内语音识别IspRecognizer;其次,应用程序需调用ISpRecognizer::SetInput方法(参见IspObjectToken接口的说明)来设置音频输入流;最后,应用程序可调用ISpRecognizer::CreateRecoContext来获取IspRecoContext接口。
下一步需要为应用程序感兴趣的事件设置通知消息。IspRecognizer也是一种IspEventSource接口,自然是一种IspNotifySource接口,因此,应用程序能够从其IspRecoContext接口中调用IspNotifySource的方法来指定IspRecoContext所需的消息应通知到何处。调用ISpEventSource::SetInterest方法可以设定什么样的事件需要被通知。最重要的事件是SPEI_RECOGNITION,它标识了IspRecognizer已从IspRecoContext中识别了一些语音。Speech SDK文档中SPEVENTENUM的说明提供了其他语音识别事件的详细说明。
最后需要说明的是,应用程序必须创建、装载并激活一个IspRecoGrammar接口。该接口从本质上说明了什么语音类型,即口述或命令和控制语法。应用程序首先应调用ISpRecoContext::CreateGrammar方法创建一个IspRecoGrammar接口。然后装载合适的语法,调用ISpRecoGrammar::LoadDictation方法可装载口述语法,调用ISpRecoGrammar::LoadCmdxxx方法可装载命令和控制语法。最后,为了激活语法并启动识别,应用程序应该调用ISpRecoGrammar::SetDictationState方法设置口述状态,或者调用ISpRecoGrammar::SetRuleState方法或 ISpRecoGrammar::SetRuleIdState方法设置命令和控制状态。
当应用程序通过请求的通知机制得到通知消息时,SPEVENT结构的lParam成员包含了一个IspRecoResult接口,应用程序能从中确定用IspRecoContext中的哪个IspRecoGrammar接口已识别了什么语音。
无论共享的还是进程内的IspRecognizer接口都能拥有多个与其关联的IspRecoContexts接口,并且每一个接口都能通过自己的事件通知方式得到相应的消息。可以从一个IspRecoContext接口中创建多个IspRecoGrammars接口,不同的接口可用于识别不同的语音类型。
11.1.3 安装Speech SDK
进行Text-To-Speech编程之前,必须先下载Miscrosoft Speech SDK,并将它安装到你的系统中。
Miscrosoft Speech SDK的下载网址是http://www.microsoft.com/speech。至笔者编写本章时为止,最新的Speech SDK版本是5.1版。下载的speechsdk51.exe是一个可执行的文件包压缩文件。运行它,将安装文件释放到一个临时目录中,执行其中的Microsoft Speech SDK 5.1.msi,将Speech SDK安装到相应的目录中。一般选用默认的安装目录(C:/Program Files/Microsoft Speech SDK 5.1)。
Speech SDK支持的默认语言是英语,即安装Speech SDK后,系统还只能支持英语的语音。要使系统支持中文和日文语音,还需要下载安装相应的语言包。从相同的网址中下载语言包speechsdk51LangPack.exe。运行它,将安装文件释放到一个临时目录中,执行其中的Microsoft Speech SDK 5.1 Language Pack.msi,将中、日文支持安装到系统中。
安装好Speech SDK后,语音控制程序将被添加到系统的控制面板中。利用该控制程序可以设置语音识别和文字-语音转换的各项属性,包括语言/语音、语速和输入设备等