资源描述
摘 要
在进行信息处理时,经常需要进行输入法切换,在录入时还要校验其录入的正确性。本课题开发的带语音校对功能的全拼五笔输入法是一款旨在提高用户汉字录入速度的输入法软件。
本课题分为两个模块,即全拼五笔输入法模块的设计实现和语音校对功能模块的设计实现。各模块相互独立,同时又构成统一的整体。
Microsoft Speech SDK 5.1是微软公司提供的、包含语音识别和语音合成功能的软件开发工具包,它提供了英文和中文两种TTS(Text To Speech)引擎,当应用程序需要发声的时候就调用语音合成引擎(Speech Synthesis Engine)进行语言合成,使用Microsoft Speech SDK 5.1可以开发中文和英文发声的应用程序,实现文本到语音的转换。利用Microsoft Speech SDK 5.1可以把TTS技术集成到应用软件中,并可以自由发布。语音合成技术是信息处理领域的一个重要分支,实现计算机文本语音合成,就是让计算机开口说话,这也是人工智能的一个重要研究方向。
本课题重点介绍了在实际研究开发过程中采用的一些技术手段,并简要介绍了一些相关理论基础、软件实现的主要方法、同时指出了软件开发有待于进一步完善发展的方向。
关键词: Speech SDK;文本转换语音;输入法
ABSTRACT
When we process information, we have to shift the input methods and checkout the correctness of our input. This topic developed Wu Bi Input Method with speech correction function aimed to improve Chinese input speed.
This topic development with speech proof Piece together Wu Bi Input Method divides into two modules, namely Piece together Wu Bi Input Method design realizing and speech proof design realizing. Various modules are mutually Independent, at the same time also constitutes the unified whole.
Microsoft Speech SDK 5.1 is software was offered by Microsoft corp.which including Speech Recognition and Speech Synthesize Engine. It was offered English and Chinese version TTS. When the application require speak, then the Speech Synthesis Engine will be available to synthesize the speech.Use Microsoft Speech SDK 5.1 exploitation an application for speak Chinese and English realizing text to speech, realized transferring from text to speech. Using Microsoft Speech SDK 5.1it can integrate TTS technical into application software and release freely. Speech Synthesis technical is a very important ramification of dealing with information. Realize synthesizing computer text and speech, it is let computer can speak out .And it is also an important Artificial intelligence research direction
This article introduce with emphasis uses some technical methods in the actual research performance history, and briefly introduced some related rationales, and software implement method, at the same time had pointed out that this topic waits for further consummates the development the direction.
Key words: Microsoft Speech SDK; Text To Speech; Input Method
25
目 录
摘 要 I
ABSTRACT II
1 概述 1
1.1国内外研究动态 1
1.2系统特点和设计基本原则 2
1.3开发工具的选择 2
2 全拼五笔输入法的设计 3
2.1全拼五笔输入法设计基本原则. 3
2.2全拼五笔输入法软件设计方法 3
3 语音功能模块技术难点 5
3.1 COM理论基础 5
3.2 COM对象的创建与使用 8
4 开发环境及配置 11
4.1 Text-To-Speech API 11
4.2语音识别API 12
4.3 Speech SDK安装与设置 13
5 语音校对功能模块设计与实现 15
5.1创建用户界面控件的选择与处理 15
5.2消息映射及处理 16
5.3语音编程的实现 18
5.4系统中多音字的处理策略 21
5.5标点符号的处理策略 22
6 结论 23
参考文献 24
致 谢 25
1 概述
在计算机广泛应用于社会各行各业的今天,大多数计算机所作的工作是进行信息处理。要进行信息处理,首先我们要做的工作是把收集的数据录入到计算机中,正因为如此,汉字录入的重要性和必要性表现得越来越突出,提高我们的汉字录入速度已成为很多人的梦想。
五笔输入法是目前中文输入法中录入速度最快的输入法之一,五笔输入法具有与读音无关、重码率低、录入速度快、便于盲打等优点。全拼输入法具有简单快速、与读音相关、但重码率高的特点。
在进行信息处理时,经常需要在多种输入法间进行切换,在录入时还要校验其录入的正确性。我们所开发的带语音校对功能的全拼五笔输入法就是从方便用户录入出发,以提高数据录入为目的。
1.1国内外研究动态
汉字是世界上使用人口最多的文字,是中国人民宝贵的文化遗产,也是联合国的工作语言之一。随着科学技术的发展,计算机技术的不断普及应用,在信息化时代里,如何使汉字迅速准确地输入计算机,是每个汉字输入法创造者日思夜想的问题。
中文输入法技术的发展,可以追溯到上个世纪80年代初。由最初的单词输入,发展到现在的词组甚至是整句输入,中文输入法的发展,可以用“百花齐放”和“万码奔腾”来形容。在20多年间,共出现过近千种编码方法,民间开发的各种版本更是不计其数。目前,音码和形码已占领了中文输入法应用的绝对主流。
音码输入法的主流当属微软拼音2003、紫光输入法、拼音加加等,这类输入法最大的特点是完全忠于拼音的操作方式,提供了一个很大的词库。
形码输入法的主流当属王码五笔输入法、万能五笔输入、极品五笔输入法等等。这类输入法具有与读音无关、重码率低、录入速度快、便于盲打等优点而成为用户喜欢的输入法。
输入法经过十几年的发展可以说已经相当成熟了,但在实际的工作中进行信息处理时存在需要在不同的输入法间进行切换,以及校验信息录入的正确性等问题。为了更好的解决键盘输入法的各种问题,带语音校对功能的全拼五笔输入法的开发思想应运而生。
1.2系统特点和设计基本原则
输入法发展方向是功能的多元化。这方面的代表是“万能码”,万能码是一种将拼音、五笔结合的一种字词输入法,不需要切换输入法就可以使用多种功能,例如输入“苹果”这个词,可以键入它的拼音“pingguo”,也可以用五笔编码输入,因此对于已经习惯传统输入法的拼音或者五笔用户,很容易使用万能码。带语音校对功能的全拼五笔输入法设置了万能码Z键,用于在全拼输入法和五笔输入法之间进行切换,从而方便用户使用。带语音校对功能的全拼五笔输入法的设计原则如下:
1、实现单个汉字及词组的五笔输入。
2、当使用Z键时自动转到全拼输入法,以辅助五笔输入法。
3、实现全角半角之间的切换。
4、输入时自动发声以利校对。
1.3开发工具的选择
目前的Windows应用程序开发平台主要有:微软公司(Microsoft Corporation)的Visual C++、Visual Basic ,以及布朗公司(Borland Corporation)的Delphi等。为使本Windows应用程序做得比较专业,以及考虑到该程序代码可以用到以后的相应的Windows应用程序开发当中,应当使程序代码具有较好的可重用性,我们选择了面向对象的程序开发语言——Visual C++。
Visual C++是一个C++语言的开发工具,而C++语言已成为了全世界专业编程人员的首选语言,C++已经有了一个公认的标准,并且许多编译器支持该标准中的绝大多数特性。Visual C++友好的用户界面和面向对象开发的支持成为本次开发的首选工具。
2 全拼五笔输入法的设计
汉字输入法研究是中文信息处理的一个重要课题。根据输入设备的不同可以将常用的汉字输入法分为三类:键盘输入法、笔输入法和语音输入法。他们的共同特征是:在联机状态下,将汉字的不同表现形式(键盘编码形式、笔顺形式和语音形式)通过软件转换形成计算机可以识别的内部表示。汉字输入法设计的总体思路是:通过不同的方式获取用户输入的信息,再经过转换引擎将输入的信息转换成为其对应的汉字。如果出现多种转换结果,则出现选择窗口,最后由用户选择所需要的汉字。对于中文Windows操作系统而言,则将结果汉字字符串转换成相应的汉字字符消息,发送到应用程序窗口,完成汉字的输入过程。
实现汉字输入法规范的方式是利用Windows所提供的输入法管理器和输入法编辑器的结构,使用Windows IME API(应用程序接口)进行输入法编程实现。
2.1全拼五笔输入法设计基本原则.
全拼五笔输入法设计的总体思路是:通过不同的方式获取用户输入的信息,再经过转换引擎将输入的信息转换成为其对应的汉字,如果出现多种转换结果,则出现选择窗口,最后由用户选择所需要的汉字。对于中文Windows操作系统而言,则将结果汉字字符串转换成相应的汉字字符消息,发送到应用程序窗口完成汉字的输入过程。
2.2全拼五笔输入法软件设计方法
在Windows平台中输入法是一个动态链接库,这个动态链接库以ime作为扩展名,主要由两部分组成:用户接口(User Interface)和转换接口(Conversion Interface)。转换接口是一批可供IMM或应用程序调用的接口函数(Interface Functions) 集合,IMM和应用程序通过调用这批接口函数来实现从输入码到机内码的转换和输入法状态的控制。用户接口是由一些可见或不可见的窗口组成,这些窗口如图1 所示,主要的作用是接收各种消息以及通过各种窗口的显示,让用户随时了解输入法的当前状态。
在Windows的User.exe中有一个类“ime”,IME窗口就是基于这个类的一个不可见窗口,这个窗口处理输入法中的所有用户接口以及应用程序和IMM发送到输入法的所有消息。应用程序可以创建一个IME窗口,这个IME窗口用来管理应用程序所有的可选输入法,通过处理消息WM_IME_SELECT可以在多个输入法间切换。所以,IME窗口是为所有的输入法共享的一个类。用户界面窗口是一个具体的输入法总控窗口,它是IME窗口的子窗口。它的作用是接收由IMM和应用程序发送来的消息,并根据消息进行相应的处理。另外,它还创建状态窗口(status window)、编码窗口(composition window)和选择窗口(candidate window),并保存这些窗口的基本数据。IME的设计,就是为用户接口和转换接口编写代码,完成IME界面的表示和输入码到机内码的转换工作。所以,输入法的设计主要是根据Windows系统所定义的IME-IMM结构,在这个结构的框架内为图1所示的各个部分编写代码,其中最为重要的是转换接口代码的编写。在DDK中,Microsoft提供了一个区位码输入法的所有代码,在此基础上进行输入法的设计,由于大量代码可以重用,从而大大节省了开发的时间
用户接口和转换接口其详细的结构如图1所示。
IME转换接口
IMM
应用程序窗口
缺省的IME窗口
IME用户界面窗口若悬河
编码窗口
状态窗口
选择窗口
原始
键盘
输入
消息
键盘动作消息
汉字字符消息
字
符
消
息
IME
相关
消息
IME
相关
消息
IME用户接口
图1 IME结构和消息传递过程
3 语音功能模块技术难点
Microsoft Speech SDK提供关于语音(Speech)处理的一套应用程序编程接口SAPI(Speech Application Programming Interface)。SAPI提供了实现文字语音转换(Text-to-Speech)和语音识别(Speech Recognition)程序的基本函数,大大简化了语音编程的难度,降低了语音编程的工作量。由于Microsoft Speech SDK是以COM接口的方式提供服务的,掌握COM(Component Object Model)的有关基础知识是必需的。
3.1 COM理论基础
COM是一种跨应用和语言共享二进制代码的方法。与C++不同,它提倡源代码重用。Windows使用DLLs(动态链接库)在二进制级共享代码。这也是Windows程序运行的关键,重用kernel32.dll, user32.dll等。但DLLs是针对C接口而写的,它们只能被C或理解C调用规范的语言使用。由编程语言来负责实现共享代码,而不是由DLLs本身,这样DLLs的使用受到限制。
COM通过定义二进制标准解决了这些问题,即COM明确指出二进制模块(DLLs和EXEs(可执行文件))必须被编译成与指定的结构匹配。这个标准也确切规定了在内存中如何组织COM对象。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的细节技术。Microsoft 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的基本概念:
(a)对象与接口
一个对象能暴露任何数量的接口。例如,所有的对象都必须暴露IUnknown接口,它们一般还暴露至少一个其他的接口,它们也可能暴露更多的接口。为了使用一个特定的方法,首先必须获得正确的接口指针。
多个不同的接口可以暴露同一个接口,一个接口就是一组执行特定操作的方法。接口定义只是指定了方法的调用语法和它们的一般功能。任何需要支持一组特定操作的COM对象都可通过暴露一个合适的接口来实现这些特定的操作。有些接口非常专业,仅仅由单一的对象来暴露。有些接口在许多场合下都非常有用,它们可由许多的对象来暴露。最极端的例子是IUnknown接口,所有的COM对象都需要暴露它。
如果一个对象暴露一个接口,它必须支持接口定义的每一个方法。但是,不同的对象实现一个特定方法的方式是不同的。不同的对象可能使用不同的算法来实现最后的结果。有时一个对象暴露一组通用的方法,但往往只需要支持其中的一部分方法。可是其他的未被支持的方法也需要能被成功调用,只是它们都只返回E_NOTIMPL。
(b)GUID
全球惟一标识符(Globally Unique Identifiers,GUIDs)是COM编程模型的关键部分。从本质上来说,GUID是一个128位的结构。然而,GUID在创建时必须保证不能出现两个相同的GUID。COM在如下的两个方面广泛地使用GUID:一方面唯一地标识一个特定的COM对象,赋予一个COM对象的GUID叫做一个类标识(class ID,CLSID)。当需要创建一个相关的COM对象的实例时,需要使用CLSID。另一方面唯一地标识一个特定的COM接口。与一个COM接口相关的GUID叫做接口标识(interface ID,IID)。当从一个对象中请求一个特定接口时,需要使用IID。不管哪个对象暴露接口,该接口的IID都是相同的。
虽然GUID是一种结构,但经常表示为对应的字符串。最常用的GUID字符串形式是“{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}”,其中x为一个十六进制整数。由于实际的GUID很长且容易写错,所以一般还提供一个等价的名称。在调用CoCreateInstance通常的命名惯例是之类的函数时,可以使用这个名称而不使用实际的GUID结构。分别在对象或接口的描述名称前加上CLSID_或IID_作为前缀。例如,ISpVoice接口的CLSID的名称是CLSID_ISpVoice。
(c) HRESULT类型值
所有的COM方法都返回一个32位的HRESULT类型的值。对大部分方法而言,HRESULT本质上是一个包含独立的两部分信息的结构:
1)方法调用成功了还是失败了。
2)关于方法所支持操作的结果的更详细信息。
有些方法仅仅返回在Winerror.h中定义的标准的HRESULT类型值。然而,方法可以返回自己定义的HRESULT类型值,以提供更专用的信息。虽然HRESULT类型值常用来返回错误信息,但不要将它们看成错误码。由于说明成功或失败的位在包含详细信息的数据中是分别存储的,所以HRESULT类型值可以包含任何数量的成功和失败代码。作为一种惯例,成功码的名称具有S_前缀,错误码的名称具有E_前缀。
COM方法可能返回许多不同的成功码或错误码,因此必须很小心地测试HRESULT类型的值。例如,假设一个方法在文档中说明成功,返回S_OK,不成功则返回E_FAIL。这时,该方法可能还会返回其他的成功码或错误码。一种可靠的测试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方法,它减少一次对象的引用计数并返回当前的引用计数。
(d) 指针地址
C或C++开发人员熟悉普通的指针,但是COM经常使用另外的间接层。这种间接的第二层用两个星号(**)跟着类型声明来表示。变量名一般使用“pp”作为前缀。在上面的例子中,参数ppReturnedDeviceInterface表示指向IDirect3DDevice8 接口的指针的地址。
与C++不同,不需要直接访问COM对象的方法,而必须获取指向方法的接口的指针。然后像调用指向C++方法的指针一样来调用方法。例如,使用如下的语法来调用方法
IMyInterface::DoSomething method:
IMyInterface *pMyIface;
.
.
.
pMyIface->DoSomething(...);
这样做的原因是不直接创建接口指针,而是必须调用不同的方法来创建接口指针。为了使用这种方法来获取接口指针,应声明一个指向需要的接口的指针变量,并将该指针变量的地址,即一个指针的地址,传递给该方法。当该方法返回时,该变量将指向你要求的接口,可以使用该指针来调用接口的任何方法。
3.2 COM对象的创建与使用
(A)创建COM对象:以下是在最常用的两种方法:
(1)直接法:将对象的CLSID(类标识)传给CoCreateInstance函数。该函数将创建对象的一个实例,并返回指向你所指定接口的指针。
(2)间接法:调用一个特殊的方法或函数来创建对象。这类方法创建对象并返回该对象的接口。使用这种方式来创建对象时,通常并不能指定需要返回的接口。
创建对象之前,必须调用CoInitialize函数来初始化COM。如果使用间接法来创建对象,对象的创建方法将自动完成COM初始化。
如果使用CoCreateInstance来创建对象,则必须明确地调用CoInitialize。当完成所有的COM工作后,必须调用CoUninitialize来卸载COM。如果调用了CoInitialize,则必须对应地调用一次CoUninitialize。需要明确,初始化COM的应用程序在其启动过程中初始化COM,在其清除过程中卸载COM。
用CoCreateInstance 来创建一个COM对象的实例需要使用该对象的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;
}
用间接法创建对象往往简单得多,只要将接口指针的地址传给对象的创建方法,该方法就会创建对象并返回接口指针。间接地创建对象时,一般不能选择返回哪个接口,但是可以指定如何来创建对象。
(B)COM接口的使用:
IUnknown接口
所有的COM对象都支持一个叫做IUnknown的接口。该接口提供了对对象的生命期的控制和检索对象的其他接口的方法,IUnknown接口有以下3个方法。
(1)AddRef:当一个接口或另一个应用程序与对象绑定时,对象的引用计数加1。
(2)QueryInterface:查询对象所支持的功能,并请求指向指定的接口的指针。
(3)Release:对对象的引用计数减1。当引用计数变为0时,对象将被释放。
AddRef和Release方法维护对象的引用计数。例如,当创建一个对象时,该对象的引用计数变为1。每次一个函数返回一个指向该对象的接口的指针时,该函数都必须调用AddRef来增加其引用计数。AddRef的每一次调用都必须与Release的调用相匹配。在指针被释放前必须对该指针调用Release。当一个对象的引用计数变为0时,该对象将被释放,它的所有接口都将变为无效接口。因此在释放该接口的指针之前,应用程序必须调用Release来减少引用计数。获得接口指针后,可以用该指针来访问接口的任何方法。
在许多情形中,从创建QueryInterface方法用来确定一个对象是否支持指定的接口。如果一个对象支持一个接口,QueryInterface返回一个指向该接口的指针。然后就可以使用该接口的方法来与对象进行通信。如果QueryInterface成功地返回一个指向接口的指针,它将明确地调用AddRef来增加引用计数方法接收到的接口指针就是所需要的。实际上,只暴露一个除IUnknown之外的接口的对象是很不常见的。相反,许多的对象暴露多个接口,需要指向这些接口的多个指针。如果需要创建方法所返回的接口之外的更多的接口,则并不需要再创建一个新的对象。可以使用对象的IUnknown::QueryInterface方法来请求其他接口的指针。
如果使用CoCreateInstance来创建对象,则可以请求一个IUnknown接口指针,然后调用IUnknown::QueryInterface方法来请求需要的每一个接口。然而,当只需要一个接口时,这种方法显得很不方便。而且,如果使用不允许指定哪个接口应该返回的创建方法时,这种方法更不能工作。在实践中,经常不需要获得一个明确的IUnknown指针,因为所有的COM接口都是从IUnknown接口继承或扩展而来的。
(C)COM对象的生命期:
当对象被创建时,系统将分配必需的内存资源。当一个对象不再需要时,应该删除它,系统将收回它所占有的内存,以用于其他目的。对于C++对象,应直接使用new 和delete操作符来控制对象的生命期。COM不允许直接创建或删除对象。其原因是同一对象可能被多个应用程序所使用。如果其中的一个应用程序要删除该对象,其他的应用程序就可能失败。实际上,COM采用引用计数系统来控制对象的生命期。
对象的引用计数就是其中的接口被请求的次数。接口每被请求一次,其引用计数都将增加。当不再需要接口时,应用程序将释放该接口,并减少其引用计数。只要引用计数大于0,对象将保留在内存中。当引用计数变为0时,对象将释放自己。不必关心对象的引用计数,只需要正确地获取和释放对象的接口,对象将具有适当的生命期。
合理地处理引用计数对COM编程来说是非常重要的,处理不当将导致内存泄漏。COM编程人员最常见的错误是不释放接口。当出现这样的错误时,引用计数将永远不能变为0,对象将不确定地保留在内存中。
只要获得一个新的接口指针,引用计数就必须调用IUnknown::AddRef 来增加。但是,应用程序通常不需要调用该方法。如果通过调用一个对象创建方法来获得接口指针,对象将自动地增加引用计数。如果用其他的方法来创建接口指针,比如拷贝已有的指针,就必须明确地调用IUnknown::AddRef。否则,当释放原来的接口指针时,得到的对象可能被破坏,即使还需要使用该指针的拷贝。
不论明确地或对象自动地增加了引用计数,都必须释放接口指针。当不再需要接口指针时,调用IUnknown::Release来减少引用计数。一种常用的方法是,将所有的接口指针初始化为NULL,并在释放接口后将它们重新设为NULL。这样可以在清除代码中测试所有的接口指针。那些不为NULL的接口指针就是仍然活动的,需要在退出应用程序之前释放它们[1]。
4 开发环境及配置
微软的Speech SDK 5.1是微软视窗环境的开发工具包。应用程序通过API层和SAPI(SpeechAP1)通信,语音引擎则通过DDI层SAPI进行交互。和SAPI提供应用程序和语音引擎之间的高层接口,它实现并隐藏了控制和管理不同语音引擎的实时操作的底层技术细节。SAPI的结构如图2所示[2][13]。
应用程序
应用程序
SAPI运行时库
设备驱动程序接口DDI
语音识别引擎
TTS引擎
应用程序接口API
图2 SAPI系统结构
4.1 Text-To-Speech API
应用程序通过ISpVoice组件对象接口(Component Object Model Interface)来控制TTS。应用程序创建了IspVoice对象后,调用接口IspVoice的方法ISpVoice::Speak,就能产生朗读指定的文字的声音。IspVoice接口还提供了其他一系列的方法来改变声音和其合成特征,比如控制语速的ISpVoice::SetRate,控制输出音量的ISpVoice::SetVolume和改变当前语音的ISpVoice::SetVoice。
在输入用于朗读的文字中还可插入一系列的特殊SAPI控制符,用来控制输出声音的实时合成特性,如语音、语调、重音、语速和音量等。合成标志文件sapi.xsd用来说明声音的合成特性。sapi.xsd是一种标准XML(eXtensible Markup Language,可扩展的标记语言)格式文件,它与特定的引擎或当前正在使用的语音无关,是一种简单而功能强大的定制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和mFormatConverter。
4.2语音识别API
IspRecoContex是语音识别的主要接口。它也是一种ISpEventSource接口,提供了为请求的语音识别事件接收通知消息的基本载体。
有两种不同的语音识别引擎(IspRecognizer),即共享语音识别引擎(shared speech recognition engine)和进程内语音识别引擎(InProc speech recognition engine)。应用程序可以选择其中的一种。
一般推荐使用共享语音识别引擎,这种引擎能被多个应用程序共享。创建共享IspRecognizer的IspRecoContext接口很简单,应用程序只需指定参数为组件的CLSID_SpSharedRecoContext并调用COM的CoCreateInstance函数即可。这时,SAPI将设置音频输入流为SAPI的默认音频输入流。
对于单独运行于一个系统中的大型服务器应用程序,其运行效率是很重要的。这时使用进程内语音识别引擎更合适。使用进程内语音识别引擎有3个步骤:首先,应用程序需指定参数为组件的CLSID_SpInprocRecoInstan并调用COM的函数来创建其自己的进程内语音识别IspRecognizer;其次,应用程序需调用SetInput方法来设置音频输入流;最后,应用程序可调用ISpRecognizer::CreateRecoContext来获取IspRecoContext接口。
下一步需要为应用程序感兴趣的事件设置通知消息。IspRecognizer也是一种IspEventSource接
展开阅读全文