__2017-12-11 如一模式识别研究

如一模式识别研究

语音识别>>TTS/windows语音初步

转自:http://tsing01.blog.163.com/blog/static/20595728320124393453839/

一、SAPI简介

软件中的语音技术包括两方面的内容,一个是语音识别(speech recognition) 和语音合成(speech synthesis)。这两个技术都需要语音引擎的支持。微软推出的应用编程接口API,虽然现在不是业界标准,但是应用比较广泛。

SAPI全称 The Microsoft Speech API.相关的SR和SS引擎位于Speech SDK开发包中。这个语音引擎支持多种语言的识别和朗读,包括英文、中文、日文等。

SAPI包括以下组件对象(接口):

(1)Voice Commands API。对应用程序进行控制,一般用于语音识别系统中。识别某个命令后,会调用相关接口是应用程序完成对应的功能。如果程序想实现语音控制,必须使用此组对象。

(2)Voice Dictation API。听写输入,即语音识别接口。

(3)Voice Text API。完成从文字到语音的转换,即语音合成。

(4)Voice Telephone API。语音识别和语音合成综合运用到电话系统之上,利用此接口可以建立一个电话应答系统,甚至可以通过电话控制计算机。

(5)Audio Objects API。封装了计算机发音系统。

SAPI是架构在COM基础上的,微软还提供了ActiveX控件,所以不仅可用于一般的windows程序,还可以用于网页、VBA甚至 EXCEL的图表中。如果对COM感到陌生,还可以使用微软的C++ WRAPPERS,它用C++类封装了语音SDK COM对象。

二、安装SAPI SDK。

首先从这个站点下载开发包:http://www.microsoft.com/downloads/details.aspx?FamilyID=5e86ec97-40a7-453f-b0ee-6583171b4530&DisplayLang=en

下载整个SDK5.1(如果不能下,可能地址改变了,也只要去掉一些后搜SDK就行了)

Microsoft Speech SDK 5.1添加了Automation支持。所以可以在VB,ECMAScript等支持Automation的语言中使用。

版本说明:

Version: 5.1

发布日期: 8/8/2001

语音: English

下载尺寸: 2.0 MB - 288.8 MB

这个SDK开发包还包括了可以随便发布的英文和中文的语音合成引擎(TTS),和英文、中文、日文的语音识别引擎(SR)。

系统要求98以上版本。编译开发包中的例子程序需要vc6以上环境。

******下载说明******:

(1)如果要下载例子程序,说明文档,SAPI以及用于开发的美国英语语音引擎,需要下载SpeechSDK51.exe,大约68M。

(2)如果想要使用简体中文和日文的语音引擎,需要下载SpeechSDK51LangPack.exe。大约82M。

(3)如果想要和自己的软件一起发布语音引擎,需要下载SpeechSDK51MSM.exe,大约132M。

(在这个地址,我未能成功下载)。

(4)如果要获取XP下的 Mike 和 Mary 语音,下载Sp5TTIntXP.exe。大约3.5M。

(5)如果要获取开发包的文档说明,请下载sapi.chm。大约2.3M。这个在sdk51里面已经包含。

下载完毕后,首先安装SpeechSDK51.exe,然后安装中文语言补丁包SpeechSDK51LangPack,然后展开

msttss22l,自动将所需dll安装到系统目录。

三、配置vc环境

在vc6.0的环境下编译语音工程,首先要配置编译环境。假设sdk安装在d:/Microsoft Speech SDK 5.1/路径下,打开工程设置对话框,在c/c++栏中选择Preprocessor分类,然后在"附加包含路径"中输入

d:/Microsoft Speech SDK 5.1/include

告诉vc编译程序所需的SAPI头文件的位置。

然后切换到LINK栏,在Input分类下的附加库路径中输入:

d:/Microsoft Speech SDK 5.1/lib/i386

使vc在链接的时候能够找到sapi.lib。

四、语音合成的应用。即使用SAPI实现TTS(Text to Speech)。

一个最简单的例子

先看一个入门的例子:

#include

int main(int argc, char * argv[])

{

ISpVoice * pVoice = NULL;

//COM初始化:

if (FAILED(::CoInitialize(NULL)))

return FALSE;

//获取ISpVoice接口:

HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void

**)&pVoice);

if ( SUCCEEDED( hr ) )

{

hr = pVoice->Speak(L"Hello world ", 0, NULL);

pVoice->Release();

pVoice = NULL;

}

//千万不要忘记:

::CoUninitialize();

return TRUE;

}

(这样之后就可以播放了,如果安装了SpeechSDK51LangPack包,只要改windows设置:设置-控制面板-语音-<文字-语音转换>中的语音选择改为Microsoft Simplified Chinese即可;且XP环境下可以不需要安装语音引擎, 安装完SDK之后可以看其例子,更详细)

短短20几行代码就实现了文本语音转换,够神奇吧。SDK提供的SAPI是基于COM封装的,无论你是否熟悉COM,只要按部就班地用 CoInitialize(), CoCreateInstance()获取IspVoice接口就够了,需要注意的是初始化COM后,程序结束前一定要用 CoUninitialize()释放资源。

上述程序的流程是获取IspVoice接口,然后用ISpVoice::Speak()把文本输出为语音,可见,程序的核心就是IspVoice接口。除了Speak外IspVoice接口还有许多成员函数,具体用法请参考SDK的文档。下面择要说一下几个主要函数的用法: HRESULT Speak(const WCHAR *pwcs,DWORD dwFlags,ULONG *pulStreamNumber);

功能:就是speak了

参数:

*pwcs 输入的文本字符串,必需为Unicode,如果是ansi字符串必需先转换为Unicode。

dwFlags 用来标志Speak的方式,其中SPF_IS_XML 表示输入文本含有XML标签,这个下文会讲到。

PulStreamNumber 输出,用来获取去当前文本输入的等候播放队列的位置,只有在异步模式才有用。

1、首先要初始化语音接口,一般有两种方式:

ISpVoice* pVoice;

::CoInitialize(NULL);

HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice,

(void **)&pVoice);

然后就可以使用这个指针调用SAPI函数了,例如

pVoice->SetVolume(50);//设置音量

pVoice->Speak(str.AllocSysString(),SPF_ASYNC,NULL);

另外也可以使用如下方式:

CComPtr m_cpVoice;

HRESULT hr = m_cpVoice.CoCreateInstance( CLSID_SpVoice );

在下面的例子中都用这个m_cpVoice变量。

CLSID_SpVoice的定义位于SPAI.H中。

2、获取/设置输出频率。

SAPI朗读文字的时候,可以采用多种频率方式输出声音,比如:

8kHz 8Bit Mono、8kHz 8Bit Stereo、44kHz 16Bit Mono、44kHz 16Bit Stereo等。在音调上有所差别。具体可以参考sapi.h。

可以使用如下代码获取当前的配置:

CComPtr cpStream;

HRESULT hrOutputStream = m_cpVoice->GetOutputStream(&cpStream);

if (hrOutputStream == S_OK)

{

CSpStreamFormat Fmt;

hr = Fmt.AssignFormat(cpStream);

if (SUCCEEDED(hr))

{

SPSTREAMFORMAT eFmt = Fmt.ComputeFormatEnum();

}

}

SPSTREAMFORMAT 是一个ENUM类型,定义位于SPAI.H中。每一个值对应了不同的频率设置。例如 SPSF_8kHz8BitStereo = 5

通过如下代码设置当前朗读频率:

CComPtr m_cpOutAudio; //声音输出接口

SpCreateDefaultObjectFromCategoryId( SPCAT_AUDIOOUT, &m_cpOutAudio ); //创建接口

SPSTREAMFORMAT eFmt = 21; //SPSF_22kHz 8Bit Stereo

CSpStreamFormat Fmt;

Fmt.AssignFormat(eFmt);

if ( m_cpOutAudio )

{

hr = m_cpOutAudio->SetFormat( Fmt.FormatId(), Fmt.WaveFormatExPtr() );

}

else hr = E_FAIL;

if( SUCCEEDED( hr ) )

{

m_cpVoice->SetOutput( m_cpOutAudio, FALSE );

}

3、获取/设置播放所用语音。

引擎中所用的语音数据文件一般保存在SpeechEngines下的spd或者vce文件中。安装sdk后,在注册表中保存了可用的语音,比如英文的男/ 女,简体中文的男音等。位置是:

HKEY_LOCAL_MACHINE/Software/Microsoft/Speech/Voices/Tokens

如果安装在中文操作系统下,则缺省所用的朗读语音是简体中文。SAPI的缺点是不能支持中英文混读,在朗读中文的时候,遇到英文,只能逐个字母读出。所以需要程序自己进行语音切换。

(1) 可以采用如下的函数把当前SDK支持的语音填充在一个组合框中:

// SAPI5 helper function in sphelper.h

HWND hWndCombo = GetDlgItem( hWnd, IDC_COMBO_VOICES ); //组合框句柄

HRESULT hr = SpInitTokenComboBox( hWndCombo , SPCAT_VOICES );

这个函数是通过IEnumSpObjectTokens接口枚举当前可用的语音接口,把接口的说明文字添加到组合框中,并且把接口的指针作为LPARAM

保存在组合框中。

一定要记住最后程序退出的时候,释放组合框中保存的接口:

SpDestroyTokenComboBox( hWndCombo );

这个函数的原理就是逐个取得combo里面每一项的LPARAM数据,转换成IUnknown接口指针,然后调用Release函数。

(2) 当组合框选择变化的时候,可以用下面的函数获取用户选择的语音:

ISpObjectToken* pToken = SpGetCurSelComboBoxToken( hWndCombo );

(3) 用下面的函数获取当前正在使用的语音:

CComPtr pOldToken;

HRESULT hr = m_cpVoice->GetVoice( &pOldToken );

(4) 当用户选择的语音和当前正在使用的不一致的时候,用下面的函数修改:

if (pOldToken != pToken)

{

// 首先结束当前的朗读,这个不是必须的。

HRESULT hr = m_cpVoice->Speak( NULL, SPF_PURGEBEFORESPEAK, 0);

if (SUCCEEDED (hr) )

{

hr = m_cpVoice->SetVoice( pToken );

}

}

(5) 也可以直接使用函数SpGetTokenFromId获取指定voice的Token指针,例如:

WCHAR pszTokenId[] = L"HKEY_LOCAL_MACHINE//Software//Microsoft//Speech//Voices//Tokens//MSSimplifiedChineseVoice";

SpGetTokenFromId(pszTokenID , &pChineseToken);

4、开始/暂停/恢复/结束当前的朗读

要朗读的文字必须位于宽字符串中,假设位于szWTextString中,则:

开始朗读的代码:

hr = m_cpVoice->Speak( szWTextString, SPF_ASYNC | SPF_IS_NOT_XML, 0 );

如果要解读一个XML文本,用:

hr = m_cpVoice->Speak( szWTextString, SPF_ASYNC | SPF_IS_XML, 0 );

暂停的代码: m_cpVoice->Pause();

恢复的代码: m_cpVoice->Resume();

结束的代码:(上面的例子中已经给出了)

hr = m_cpVoice->Speak( NULL, SPF_PURGEBEFORESPEAK, 0);

5、跳过部分朗读的文字

在朗读的过程中,可以跳过部分文字继续后面的朗读,代码如下:

ULONG ulGarbage = 0;

WCHAR szGarbage[] = L"Sentence";

hr = m_cpVoice->Skip( szGarbage, SkipNum, &ulGarbage );

SkipNum是设置要跳过的句子数量,值可以是正/负。

根据sdk的说明,目前SAPI仅仅支持SENTENCE这个类型。SAPI是通过标点符号来区分句子的。

6、播放WAV文件。SAPI可以播放WAV文件,这是通过ISpStream接口实现的:

CComPtr cpWavStream;

WCHAR szwWavFileName[NORM_SIZE] = L"";;

USES_CONVERSION;

wcscpy( szwWavFileName, T2W( szAFileName ) );//从ANSI将WAV文件的名字转换成宽字符串

//使用sphelper.h 提供的这个函数打开 wav 文件,并得到一个 IStream 指针

hr = SPBindToFile( szwWavFileName, SPFM_OPEN_READONLY, &cpWavStream );

if( SUCCEEDED( hr ) )

{

m_cpVoice->SpeakStream( cpWavStream, SPF_ASYNC, NULL );//播放WAV文件

}

7、将朗读的结果保存到wav文件

TCHAR szFileName[256];//假设这里面保存着目标文件的路径

USES_CONVERSION;

WCHAR m_szWFileName[MAX_FILE_PATH];

wcscpy( m_szWFileName, T2W(szFileName) );//转换成宽字符串

//创建一个输出流,绑定到wav文件

CSpStreamFormat OriginalFmt;

CComPtr cpWavStream;

CComPtr cpOldStream;

HRESULT hr = m_cpVoice->GetOutputStream( &cpOldStream );

if (hr == S_OK) hr = OriginalFmt.AssignFormat(cpOldStream);

else hr = E_FAIL;

// 使用sphelper.h中提供的函数创建 wav 文件

if (SUCCEEDED(hr))

{

hr = SPBindToFile( m_szWFileName, SPFM_CREATE_ALWAYS, &cpWavStream,

&OriginalFmt.FormatId(), OriginalFmt.WaveFormatExPtr() );

}

if( SUCCEEDED( hr ) )

{

//设置声音的输出到 wav 文件,而不是 speakers

m_cpVoice->SetOutput(cpWavStream, TRUE);

}

//开始朗读

m_cpVoice->Speak( szWTextString, SPF_ASYNC | SPF_IS_NOT_XML, 0 );

//等待朗读结束

m_cpVoice->WaitUntilDone( INFINITE );

cpWavStream.Release();

//把输出重新定位到原来的流

m_cpVoice->SetOutput( cpOldStream, FALSE );

8、设置朗读音量和速度

m_cpVoice->SetVolume((USHORT)hpos); //设置音量,范围是 0 - 100

m_cpVoice->SetRate(hpos); //设置速度,范围是 -10 - 10

hpos的值一般位于

9、设置SAPI通知消息。SAPI在朗读的过程中,会给指定窗口发送消息,窗口收到消息后,可以主动获取SAPI的事件,

根据事件的不同,用户可以得到当前SAPI的一些信息,比如正在朗读的单词的位置,当前的朗读口型值(用于显

示动画口型,中文语音的情况下并不提供这个事件)等等。

要获取SAPI的通知,首先要注册一个消息:

m_cpVoice->SetNotifyWindowMessage( hWnd, WM_TTSAPPCUSTOMEVENT, 0, 0 );

这个代码一般是在主窗口初始化的时候调用,hWnd是主窗口(或者接收消息的窗口)句柄。WM_TTSAPPCUSTOMEVENT

是用户自定义消息。

在窗口响应WM_TTSAPPCUSTOMEVENT消息的函数中,通过如下代码获取sapi的通知事件:

CSpEvent event; // 使用这个类,比用 SPEVENT结构更方便

while( event.GetFrom(m_cpVoice) == S_OK )

{

switch( event.eEventId )

{

。。。

}

}

eEventID有很多种,比如SPEI_START_INPUT_STREAM表示开始朗读,SPEI_END_INPUT_STREAM表示朗读结束等。

可以根据需要进行判断使用。

评论留言区

:
  

作者: 游客 ; *
评论内容: *
带*号为必填项目

如一模式识别更新提示

matlab在图像处理方面的应用有更新

如一模式识别 友情链接

关于本站作者     chinaw3c     mozilla