如何用微软人脸识别 sdkSpeech SDK 5.3开发语音识别

用TTS技术开发具有文本朗读功能的应用程序
TTS技术已经越来越广泛地应用到现代计算机的各个领域,本文介绍了利用微软的Microsoft Speech SDK5.1提供的TTS(text-to-speech)技术开发具有语音朗读功能的应用程序.
Create speech-enableed Apllications with TTS Technology
Abstract: The TTS technology is more and more popular in moderncomputer, this article introduces How to Create speech-enableedApllications use the Microsoft Speech SDK 5.1
Key word: Speech STTS;speech Engine
1.TTS技术概述
上世纪90年代中期以来,随着个人计算机的硬件和软件功能越来越强,和现代语音技术的发展,以前在科幻电影中才能看到的会说话的电脑已经成为现实.而TTS技术正是电脑能够说话的关键技术之一.
TTS是text-to-speech的缩写,英文也称SpeechSynthesis即语音合成.语音合成就是一个将文本转化为语音输出的过程,这个过程的工作主要是将输入的文本按字或词分解为音素,并且对文本中的数字、货币单位、单词变形以及标点等要特殊处理的符号进行分析,以及将音素生成数字音频然后用扬声器播放出来或者存为声音文件以后用多媒体软件播放.图1形象地描述了这个过程.
图1. text-to-speech实现过程
当应用程序需要发声的时候就调用语音合成引擎(SPEECH SYNTHESISENGINE)进行语言合成,将文本处理后通过扬声器用近似于人的声音“读”出来,通常还可以通过改变对语音引擎的设置改变“说话”的速度,声音频率(低沉或者尖锐),声音大小,还能模拟口形、唇形和舌位的变化对声音的影响.面前计算机通过语音合成发出的声音效果听起来就象是录音磁带发出的声音.
与一些用预先录制的声音文件实现发声的应用程序相比,TTS的发声引擎只有几兆大小,不需要大量的声音文件支持,因此可以节省很大的储存空间,并且可以朗读预先未知的任何语句.现在已经有许多应用软件应用TTS技术实现语音功能,例如一些播音软件可以用来读小说或作校对工作,还可以朗读电子邮件,一些电子词典可以读出单词,还可以用于查询中心自动播放服务信息等.
2.关于Microsoft Speech SDK
实现TTS的核心技术本身是非常复杂的,不是一般开发人员所能完成的,但是有了Microsoft SpeechSDK这样的工具的帮助,只需要在应用程序中加入一些不太复杂的命令和操作,我们就可以创建具有TTS功能的应用程序了.
国内外有许多高科技公司和科研机构致力于TTS技术的开发和研究,如微软公司、IBM公司以及国内的科大迅飞公司都有较成熟的语音产品,并且任何人都可以获得微软公司免费提供的语音软件开发工具:Microsoft Speech SDK.
目前的Microsoft Speech SDK已经有4.0,5.1等几个版本,最近还推出了适合网络语音程序开发的TheMicrosoft Speech ApplicationSDK的测试版,这些SDK主要包括语音应用程序编程接口SAPI(SAPI使我们象使用windowsAPI一样方便地在程序中使用语音功能)和微软语音识别引擎和微软语音合成引擎,还提供了进行应用设计的例子.MicrosoftSpeech SDK 5.1提供了英文和中文两种TTS引擎,即可以用它开发朗读中文和英文的应用程序,非常适合中国的开发者使用.
利用这些SDK,开发人员可以很容易地把TTS技术集成到应用软件中,并可以自由发布.另外Microsoft SpeechSDK还提供了详细的文档帮助开发者进行程序开发.
3.实现TTS的条件准备
利用Microsoft Speech SDK进行TTS语音应用程序开发前要作一些软件和硬件的准备:
1)、去/speech/下载MicrosoftSpeech SDK 5.1或其它版本SDK,并且安装到自己的机器上.
2)、Microsoft VC++ 6.0 ,service pack 3 或更新的版本.
3)、声卡和喇叭
4)、要实现该SDK的全部功能,则去/msdownload/platformsdk下载新的PlatformSDK.
另外要注意的是SAPI 5.1不支持windows 95.
4.在应用程序中实现TTS功能
微软SAPI5.1实现text-to-speech的核心是对COM接口IspVoice的应用,所以应用TTS技术最好要了解一下COM编程技术,但是即使从来没有用过COM编程,只要按照SDK的示例,也可以顺利的实现TTS的功能.
SAPI的API接口封装得很好,我们不需要了解任何发声引擎底层的工作细节.例如一旦在程序中创建了一个IspVoice对象,就可以通过调用ISpVoice::Speak实现文本朗读,通过调用ISpVoice::SetRate实现对朗读速度的控制,以及通过调用ISpVoice::SetVoice实现对朗读声音的不同人物或语言控制(男声/女生,中文/英文)等.事实上掌握对这个接口的应用就可以实现大多数TTS的应用.
在下面介绍的程序中,用VC++6.0实现了Text-to-Speech的应用.
(1)创建一个工程
建立一个工程,Projects选项卡中选择MFC AppWizard (exe);下一步中选择Dialog based类型.
(2) 设置SAPI路径
应用TTS必须用到头文件sapi.h和sapi.lib,为了能编译通过必须设置SAPI路径:
a.、 在选中tools菜单中的option子项.
b.、 选中Directories选项卡.
c.、 选中Show directries for列表框中选中includefiles项,并在Directories中添加sapi头文件路径: 例如:
C:Program FilesMicrosoftSpeech SDK 5.1Include .
d.、 选中Show directries for列表框中选中Library files项,并在Directories中添加sapiLIB文件路径: 例如:
C:Program FilesMicrosoft Speech SDK5.1Libi386 .
(3) 设计程序界面
添加如图所示的编辑框和命令按钮.
(4) 添加代码
双击speak按钮,添加朗读代码:
void CExe1Dlg::Onspeak()
UpdateData();
// 2) SAPI voice
ISpVoice * pVoice = NULL;
// 1)初始化COM
if (FAILED(CoInitialize(NULL)))
AfxMessageBox( Errorto intiliaze COM );
2)初始化SAPI
HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL,
CLSCTX_ALL,IID_ISpVoice, (void **)&pVoice);
if( SUCCEEDED( hr ) )
// 3)朗读编辑框中的文字
hr = pVoice-&Speak(m_mytext.AllocSysString(), 0, NULL);
//完成发声后可以将声音对象释放
pVoice-&Release();
pVoice = NULL;
1)与CoInitialize(NULL)成对使用,释放COM
CoUninitialize();
注: m_mytext是编辑框的变量名.
以上的代码中主要做了几件TTS最基本的事情:
1)、初始化COM ,因为SAPI是基于COM的应用,使用之前必须首先初始化COM.
2)、创建voice对象,即一个COM对象.
3)、实现朗读,即调用speak方法.
另外必须把头文件sapi.h加到前面:
编译通过后就可以运行程序了,在编辑框中输入一段文字,按speak按钮,我们就能听到电脑读出的令人兴奋的声音了.
Microsoft的TTS引擎提供了4种朗读文字的声音,其中三种英文声音: Mary(女),mike和sam(男)一种中文声音:simplyfiedChinese.因为默认的TTS引擎是英文发声,如果要想朗读中文或选择不同的人进行朗读,可以在朗读前调用ISpVoice::SetVoice方法进行声音的设定,或者在朗读前双击用控制面板中的语音图标(安装MicrosoftSpeech SDK后自动添加的),选择文字-语音转换选项卡进行默认声音的设置.
SPAI5.1不仅适合用VC++进行开发,还可以用VB、c#等进行开发,具体方法可以参考SDK帮助文档.结合windows程序设计的其它技术,只要取得文本的内容,就可以实现对对word、IE、电子邮件等各种文字的朗读.
本人利用windowsAPI剪贴板操作函数和模拟键盘的方法在朗读程序中实现了对任意文本内容的获取,包括IE、WORD、PDF等所有可以被鼠标选中的文本.另还外利用中英文内码的不同对所取得的文本进行中英文分析并按不同的语言分段,在朗读前利用ISpVoice::SetVoice方法进行中文或英文声音的设定,从而实现了中英文混合文本的朗读.合成这些方法和技术实现了一个电脑播音软件.
此外Microsoft Speech SDK5.1的语音识别技术和语音识别引擎功能也很强大,可以用它开发具有语音识别功能的程序.
用TTS技术开发具有文本朗读功能的应用程序由用户自行发布,用TTS技术开发具有文本朗读功能的应用程序内容不代表本站立场,请自行分辨真伪。下次自动登录
现在的位置:
& 综合 & 正文
VC 下Microsoft Speech SDK开发语音识别
1.首先开发得需要Microsoft Speech SDK的支持,以下是下载地址
Redistributables
Documentation
2.下载后,执行安装
下载完毕后首先安装SpeechSDK51.exe,然后安装中文语言补丁包SpeechSDK51LangPack,然后展开
speechsdk51MSM.exe,这些都是自解压文件,解压后执行相应的setup到你要的目录,默认C:/Microsoft Speech SDK 5.1.对应的开发参考手册为sapi.chm,详细描述了各个函数的细节等.
3.VC的环境配置
在应用SDK的开发前当然得需要对工程环境进行配置,我用的是VS2003(其他情况类似),配置的过程如下:
工具-&选项-&项目-&VC++目录,在"显示以下内容的目录"下拉框中选择"包含目录"项,添加一项C:/Program
Files/Microsoft
5.1/Include到目录中去。再选择"库文件"项,添加一项C:/Program
Files/Microsoft
5.1/Lib/i386到目录中去.
4.其他准备项
基础的配置已经完成,那么接下来的工作就是要包含编译的头文件了,所以先将头文件和库文件包含进来
#include &sapi.h&
#include &sphelper.h&
#include &spuihelp.h&
#pragma comment(lib,"ole32.lib")
//CoInitialize CoCreateInstance需要调用ole32.dll
#pragma comment(lib,"sapi.lib")
//sapi.lib在SDK的lib目录,必需正确配置
具体其他函数所需要的头文件可参考sapi.chm手册.
5.源文件修改项
看上去上面的部分配置完成后就大功告成了,其实还不全是,当你编译时就会出错:
c:/program files/microsoft speech sdk 5.1/include/sphelper.h(769) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
c:/program files/microsoft speech sdk 5.1/include/sphelper.h(1419) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
c:/program files/microsoft speech sdk 5.1/include/sphelper.h(2373) : error C2065: 'psz' : undeclared identifier
c:/program files/microsoft speech sdk 5.1/include/sphelper.h(2559) : error C2440: 'initializing' : cannot convert from 'CSpDynamicString' to 'SPPHONEID *'
No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
c:/program files/microsoft speech sdk 5.1/include/sphelper.h(2633) : error C2664: 'wcslen' : cannot convert parameter 1 from 'SPPHONEID *' to 'const wchar_t *'
Types poin conversion requires reinterpret_cast, C-style cast or function-style cast
Speech编写时间太早,语法不严密。而VS2003(及以上)对于语法检查非常严格,导致编译无法通过。修改头文件中的以下行即可正常编译:
修改前: const ulLenVendorPreferred = wcslen(pszVendorPreferred);
修改后: const unsigned long ulLenVendorPreferred = wcslen(pszVendorPreferred);
修改前: static CoMemCopyWFEX(const WAVEFORMATEX * pSrc, WAVEFORMATEX ** ppCoMemWFEX)
修改后: static HRESULT CoMemCopyWFEX(const WAVEFORMATEX * pSrc, WAVEFORMATEX ** ppCoMemWFEX)
修改前: for (const WCHAR * psz = (const WCHAR *)lP * psz++) {}
修改后: const WCHAR * for (psz = (const WCHAR *)lP * psz++) {}
修改前: SPPHONEID* pphoneId = dsPhoneId;
修改后: SPPHONEID* pphoneId = (SPPHONEID*)((WCHAR *)dsPhoneId);
修改前: pphoneId += wcslen(pphoneId) + 1;
修改后: pphoneId += wcslen((const wchar_t *)pphoneId) + 1;
好了,编译通过,下面可以正式编写程序了。
6:语音朗读:SAPI实现TTS(Text to Speech)
首先要初始化语音接口,一般有两种方式:
ISpVoice* pV
::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&ISpVoice&
hr = m_cpVoice.CoCreateInstance(CLSID_SpVoice );
在下面的例子中都用这个m_cpVoice变量。CLSID_SpVoice的定义位于sapi.h中。
获取/设置输出频率。
SAPI朗读文字的时候,可以采用多种频率方式输出声音,比如:8kHz 8Bit Mono、8kHz 8BitStereo、44kHz 16BitStereo等,在音调上有所差别。具体可以参考sapi.h。
可以使用如下代码获取当前的频率配置:
CComPtr&ISpStreamFormat& cpS
HRESULT hrOutputStream =m_cpVoice-&GetOutputStream(&cpStream);
if (hrOutputStream ==S_OK)
CSpStreamFormat F
hr = Fmt.AssignFormat(cpStream);
if (SUCCEEDED(hr))
SPSTREAMFORMAT eFmt = puteFormatEnum();
SPSTREAMFORMAT 是一个ENUM类型,定义位于sapi.h中,这样eFmt就保存了获得的当前频率设置值。每一个值对应了不同的频率设置。
通过如下代码设置当前朗读频率:
CComPtr&ISpAudio&
m_cpOutA //声音输出接口
SpCreateDefaultObjectFromCategoryId( SPCAT_AUDIOOUT,&m_cpOutAudio ); //创建接口
SPSTREAMFORMAT eFmt = SPSF_8kHz8BitM //SPSF_8kHz 8Bit Mono这个参数可以参考sapi.chm手册
CSpStreamFormat F
Fmt.AssignFormat(eFmt);
if (m_cpOutAudio )
hr = m_cpOutAudio-&SetFormat(Fmt.FormatId(), Fmt.WaveFormatExPtr() );
hr = E_FAIL;
if(SUCCEEDED( hr ) )
m_cpVoice-&SetOutput( m_cpOutAudio, FALSE );
获取/设置播放所用语音。
引擎中所用的语音数据文件一般保存在SpeechEngines下的spd或者vce文件中。安装sdk后,在注册表中保存了可用的语音,比如英文的男/女,简体中文的男音等。位置是:
HKEY_LOCAL_MACHINE/Software/Microsoft/Speech/Voices/Tokens
SAPI的缺点是不能支持中英文混读,在朗读中文的时候,遇到英文,只能逐个字母读出。所以需要程序自己进行语音切换。
(1) 可以采用如下的函数把当前SDK支持的语音填充在一个组合框中:
// SAPI5helper function in sphelper.h
CWnd* m_wnd = GetDlgItem(IDC_COMBO_VOICES);
hWndCombo = m_wnd-&m_hW //组合框句柄
HRESULT hr =SpInitTokenComboBox( hWndCombo , SPCAT_VOICES );
这个函数是通过IEnumSpObjectTokens接口枚举当前可用的语音接口,把接口的说明文字添加到组合框中,并且把接口的指针作为LPARAM 保存在组合框中。
一定要记住最后程序退出的时候,释放组合框中保存的接口:
SpDestroyTokenComboBox( hWndCombo );
这个函数的原理就是逐个取得combo里面每一项的LPARAM数据,转换成IUnknown接口指针,然后调用Release函数。
(2) 当组合框选择变化的时候,可以用下面的函数获取用户选择的语音:
ISpObjectToken* pToken = SpGetCurSelComboBoxToken( hWndCombo );
(3) 用下面的函数获取当前正在使用的语音:
CComPtr&ISpObjectToken& pOldT
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);
开始/暂停/恢复/结束当前的朗读
要朗读的文字必须位于宽字符串中,所以从文本框中读取的字符串类型CString必须转换成为WCHAR型,如下(m_strText为文本框变量):
CString strS
m_strText.GetWindowText(strSpeak);
wChar[256];
memset(wChar ,0,256);
MultiByteToWideChar( CP_ACP , 0 , strSpeak , strSpeak.GetLength() , wChar , 256);
这样就将文本框中的字符串strSpeak转化为WCHAR型的wChar变量中了.
开始朗读的代码:
hr =m_cpVoice-&Speak( wChar, SPF_ASYNC |SPF_IS_NOT_XML, 0 );
如果要解读一个XML文本,用:
hr =m_cpVoice-&Speak( wChar, SPF_ASYNC |SPF_IS_XML, 0 );
暂停的代码:
m_cpVoice-&Pause();
恢复的代码:
m_cpVoice-&Resume();
结束的代码:(上面的例子中已经给出了)
hr =m_cpVoice-&Speak( NULL, SPF_PURGEBEFORESPEAK,0);
跳过部分朗读的文字
在朗读的过程中,可以跳过部分文字继续后面的朗读,代码如下:
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&ISpStream&
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文件
TCHARszFileName[256];//假设这里面保存着目标文件的路径
USES_CONVERSION;
WCHAR m_szWFileName[MAX_FILE_PATH];
wcscpy( m_szWFileName,T2W(szFileName) );//转换成宽字符串
//创建一个输出流,绑定到wav文件
CSpStreamFormat
CComPtr&ISpStream& cpWavS
CComPtr&ISpStreamFormat&
HRESULT hr =m_cpVoice-&GetOutputStream(&cpOldStream );
if (hr == S_OK) hr =OriginalFmt.AssignFormat(cpOldStream);
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( wChar, 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
9 设置SAPI通知消息。
SAPI在朗读的过程中,会给指定窗口发送消息,窗口收到消息后,可以主动获取SAPI的事件,根据事件的不同,用户可以得到当前SAPI的一些信息,比如正在朗读的单词的位置,当前的朗读口型值(用于显示动画口型,中文语音的情况下并不提供这个事件)等等。要获取SAPI的通知,首先要注册一个消息:
m_cpVoice-&SetNotifyWindowMessage( hWnd,WM_TTSAPPCUSTOMEVENT, 0, 0 );
这个代码一般是在主窗口初始化的时候调用,hWnd是主窗口(或者接收消息的窗口)句柄。WM_TTSAPPCUSTOMEVENT是用户自定义消息。在窗口响应WM_TTSAPPCUSTOMEVENT消息的函数中,通过如下代码获取sapi的通知事件:
// 使用这个类,比用 SPEVENT结构更方便
while(event.GetFrom(m_cpVoice) == S_OK )
switch( event.eEventId )
eEventID有很多种,比如SPEI_START_INPUT_STREAM表示开始朗读,SPEI_END_INPUT_STREAM表示朗读结束等。
可以根据需要进行判断使用。
7:语音识别
利用微软Speech SDK 5.1在MFC中进行语音识别开发时的主要步骤,以Speech API 5.1+VC6为例:
1、初始化COM端口
一般在CWinApp的子类中,调用CoInitializeEx函数进行COM初始化,代码如下:
::CoInitializeEx(NULL,COINIT_APARTMENTTHREADED); // 初始化COM
注意:调用这个函数时,要在工程设置(project settings)-&C/C++标签,Category中选Preprocessor,在Preprocessor definitions:下的文本框中加上“,_WIN32_DCOM”。否则编译不能通过。
2、创建识别引擎
微软Speech SDK 5.1 支持两种模式的:共享(Share)和独享(InProc)。一般情况下可以使用共享型,大的服务型程序使用InProc。如下:
hr = m_cpRecognizer.CoCreateInstance(CLSID_SpSharedRecognizer);//Share
hr = m_cpRecognizer.CoCreateInstance(CLSID_SpInprocRecognizer);//InProc
如果是Share型,可直接进到步骤3;如果是InProc型,必须使用 ISpRecognizer::SetInput 设置语音输入。如下:
CComPtr&ISpObjectToken& cpAudioT
//定义一个token
hr = SpGetDefaultTokenFromCategoryId(SPCAT_AUDIOIN, &cpAudioToken); //建立默认的音频输入对象
if (SUCCEEDED(hr)) { hr = m_cpRecognizer-&SetInput(cpAudioToken, TRUE);}
CComPtr&ISpAudio& cpA
//定义一个音频对象
hr = SpCreateDefaultObjectFromCategoryId(SPCAT_AUDIOIN, &cpAudio);//建立默认的音频输入对象
hr = m_cpRecoEngine-&SetInput(cpAudio, TRUE);//设置识别引擎输入源
3、创建识别上下文接口
调用 ISpRecognizer::CreateRecoContext 创建识别上下文接口(ISpRecoContext),如下:
hr = m_cpRecoEngine-&CreateRecoContext( &m_cpRecoCtxt );
4、设置识别消息
调用 SetNotifyWindowMessage 告诉Windows哪个是我们的识别消息,需要进行处理。如下:
hr = m_cpRecoCtxt-&SetNotifyWindowMessage(m_hWnd, WM_RECOEVENT, 0, 0);
SetNotifyWindowMessage 定义在 ISpNotifySource 中。
5、设置我们感兴趣的事件
其中最重要的事件是”SPEI_RECOGNITION“。参照 SPEVENTENUM。代码如下:
const ULONGLONG ullInterest = SPFEI(SPEI_SOUND_START) | SPFEI(SPEI_SOUND_END) | SPFEI(SPEI_RECOGNITION) ;
hr = m_cpRecoCtxt-&SetInterest(ullInterest, ullInterest);
6、创建语法规则
语法规则是识别的灵魂,必须要设置。分为两种,一种是听说式(dictation),一种是命令式(command and control---C&C)。首先 利用ISpRecoContext::CreateGrammar 创建语法对象,然后加载不同的语法规则,如下:
//dictation
hr = m_cpRecoCtxt-&CreateGrammar( GIDDICTATION, &m_cpDictationGrammar );
(SUCCEEDED(hr))
hr = m_cpDictationGrammar-&LoadDictation(NULL, SPLO_STATIC);//加载词典
hr = m_cpRecoCtxt-&CreateGrammar( GIDCMDCTRL, &m_cpCmdGrammar);
然后利用ISpRecoGrammar::LoadCmdxxx 加载语法,例如从CmdCtrl.xml中加载:
WCHAR wszXMLFile[20]=L"";
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)"CmdCtrl.xml"
, -1, wszXMLFile, 256);//ANSI转UNINCODE
hr = m_cpCmdGrammar-&LoadCmdFromFile(wszXMLFile,SPLO_DYNAMIC);
注意:C&C时,语法文件使用xml格式,参见Speech SDK 5.1 中的 Designing Grammar Rules。简单例子:
&GRAMMAR LANGID="804"&
&ID NAME="CMD" VAL="10"/&
&RULE NAME="COMMAND" ID="CMD" TOPLEVEL="ACTIVE"&
&p&尹成&/P&
&p&山东大学&/p&
&p&中科院&/p&
&/GRAMMAR&
LANGI*="804"代表简体中文,在&*&...&/*&中增加命令。
7、在开始识别时,激活语法进行识别
hr = m_cpDictationGrammar-&SetDictationState( SPRS_ACTIVE );//dictation
hr = m_cpCmdGrammar-&SetRuleState( NULL,NULL,SPRS_ACTIVE );//C&C
8、获取识别消息,进行处理
截获识别消息(WM_RECOEVENT),然后处理。识别的结果放在CSpEvent的ISpRecoResult 中。如下:
USES_CONVERSION;
switch (event.eEventId)
case SPEI_RECOGNITION:
//识别出了语音输入
m_bGotReco = TRUE;
static const WCHAR wszUnrecognized[] = L"&Unrecognized&";
CSpDynamicString dstrT
//取得识别结果
if (FAILED(event.RecoResult()-&GetText(SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, TRUE ,&dstrText, NULL)))
dstrText = wszU
dstrText.CopyToBSTR(&SRout);
Recstring.Empty();
Recstring = SR
//进一步处理
9、释放创建的引擎、识别上下文对象、语法等。调用相应的Release函数即可。
1.首先开发得需要Microsoft Speech SDK的支持,以下是下载地址
Redistributables
Documentation
2.下载后,执行安装
下载完毕后首先安装SpeechSDK51.exe,然后安装中文语言补丁包SpeechSDK51LangPack,然后展开
speechsdk51MSM.exe,这些都是自解压文件,解压后执行相应的setup程序到你要的目录,默认C:/Microsoft Speech SDK 5.1.对应的开发参考手册为sapi.chm,详细描述了各个函数的细节等.
3.VC的环境配置
在应用SDK的开发前当然得需要对工程环境进行配置,我用的是VS2003(其他情况类似),配置的过程如下:
工具-&选项-&项目-&VC++目录,在"显示以下内容的目录"下拉框中选择"包含目录"项,添加一项C:/Program
Files/Microsoft
5.1/Include到目录中去。再选择"库文件"项,添加一项C:/Program
Files/Microsoft
5.1/Lib/i386到目录中去.
4.其他准备项
基础的配置已经完成,那么接下来的工作就是要包含编译的头文件了,所以先将头文件和库文件包含进来
#include &sapi.h&
#include &sphelper.h&
#include &spuihelp.h&
#pragma comment(lib,"ole32.lib")
//CoInitialize CoCreateInstance需要调用ole32.dll
#pragma comment(lib,"sapi.lib")
//sapi.lib在SDK的lib目录,必需正确配置
具体其他函数所需要的头文件可参考sapi.chm手册.
5.源文件修改项
看上去上面的部分配置完成后就大功告成了,其实还不全是,当你编译时就会出错:
c:/program files/microsoft speech sdk 5.1/include/sphelper.h(769) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
c:/program files/microsoft speech sdk 5.1/include/sphelper.h(1419) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
c:/program files/microsoft speech sdk 5.1/include/sphelper.h(2373) : error C2065: 'psz' : undeclared identifier
c:/program files/microsoft speech sdk 5.1/include/sphelper.h(2559) : error C2440: 'initializing' : cannot convert from 'CSpDynamicString' to 'SPPHONEID *'
No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
c:/program files/microsoft speech sdk 5.1/include/sphelper.h(2633) : error C2664: 'wcslen' : cannot convert parameter 1 from 'SPPHONEID *' to 'const wchar_t *'
Types poin conversion requires reinterpret_cast, C-style cast or function-style cast
Speech代码编写时间太早,语法不严密。而VS2003(及以上)对于语法检查非常严格,导致编译无法通过。修改头文件中的以下行即可正常编译:
修改前: const ulLenVendorPreferred = wcslen(pszVendorPreferred);
修改后: const unsigned long ulLenVendorPreferred = wcslen(pszVendorPreferred);
修改前: static CoMemCopyWFEX(const WAVEFORMATEX * pSrc, WAVEFORMATEX ** ppCoMemWFEX)
修改后: static HRESULT CoMemCopyWFEX(const WAVEFORMATEX * pSrc, WAVEFORMATEX ** ppCoMemWFEX)
修改前: for (const WCHAR * psz = (const WCHAR *)lP * psz++) {}
修改后: const WCHAR * for (psz = (const WCHAR *)lP * psz++) {}
修改前: SPPHONEID* pphoneId = dsPhoneId;
修改后: SPPHONEID* pphoneId = (SPPHONEID*)((WCHAR *)dsPhoneId);
修改前: pphoneId += wcslen(pphoneId) + 1;
修改后: pphoneId += wcslen((const wchar_t *)pphoneId) + 1;
好了,编译通过,下面可以正式编写程序了。
6:语音朗读:SAPI实现TTS(Text to Speech)
首先要初始化语音接口,一般有两种方式:
ISpVoice* pV
::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&ISpVoice&
hr = m_cpVoice.CoCreateInstance(CLSID_SpVoice );
在下面的例子中都用这个m_cpVoice变量。CLSID_SpVoice的定义位于sapi.h中。
获取/设置输出频率。
SAPI朗读文字的时候,可以采用多种频率方式输出声音,比如:8kHz 8Bit Mono、8kHz 8BitStereo、44kHz 16BitStereo等,在音调上有所差别。具体可以参考sapi.h。
可以使用如下代码获取当前的频率配置:
CComPtr&ISpStreamFormat& cpS
HRESULT hrOutputStream =m_cpVoice-&GetOutputStream(&cpStream);
if (hrOutputStream ==S_OK)
CSpStreamFormat F
hr = Fmt.AssignFormat(cpStream);
if (SUCCEEDED(hr))
SPSTREAMFORMAT eFmt = puteFormatEnum();
SPSTREAMFORMAT 是一个ENUM类型,定义位于sapi.h中,这样eFmt就保存了获得的当前频率设置值。每一个值对应了不同的频率设置。
通过如下代码设置当前朗读频率:
CComPtr&ISpAudio&
m_cpOutA //声音输出接口
SpCreateDefaultObjectFromCategoryId( SPCAT_AUDIOOUT,&m_cpOutAudio ); //创建接口
SPSTREAMFORMAT eFmt = SPSF_8kHz8BitM //SPSF_8kHz 8Bit Mono这个参数可以参考sapi.chm手册
CSpStreamFormat F
Fmt.AssignFormat(eFmt);
if (m_cpOutAudio )
hr = m_cpOutAudio-&SetFormat(Fmt.FormatId(), Fmt.WaveFormatExPtr() );
hr = E_FAIL;
if(SUCCEEDED( hr ) )
m_cpVoice-&SetOutput( m_cpOutAudio, FALSE );
获取/设置播放所用语音。
引擎中所用的语音数据文件一般保存在SpeechEngines下的spd或者vce文件中。安装sdk后,在注册表中保存了可用的语音,比如英文的男/女,简体中文的男音等。位置是:
HKEY_LOCAL_MACHINE/Software/Microsoft/Speech/Voices/Tokens
SAPI的缺点是不能支持中英文混读,在朗读中文的时候,遇到英文,只能逐个字母读出。所以需要程序自己进行语音切换。
(1) 可以采用如下的函数把当前SDK支持的语音填充在一个组合框中:
// SAPI5helper function in sphelper.h
CWnd* m_wnd = GetDlgItem(IDC_COMBO_VOICES);
hWndCombo = m_wnd-&m_hW //组合框句柄
HRESULT hr =SpInitTokenComboBox( hWndCombo , SPCAT_VOICES );
这个函数是通过IEnumSpObjectTokens接口枚举当前可用的语音接口,把接口的说明文字添加到组合框中,并且把接口的指针作为LPARAM 保存在组合框中。
一定要记住最后程序退出的时候,释放组合框中保存的接口:
SpDestroyTokenComboBox( hWndCombo );
这个函数的原理就是逐个取得combo里面每一项的LPARAM数据,转换成IUnknown接口指针,然后调用Release函数。
(2) 当组合框选择变化的时候,可以用下面的函数获取用户选择的语音:
ISpObjectToken* pToken = SpGetCurSelComboBoxToken( hWndCombo );
(3) 用下面的函数获取当前正在使用的语音:
CComPtr&ISpObjectToken& pOldT
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);
开始/暂停/恢复/结束当前的朗读
要朗读的文字必须位于宽字符串中,所以从文本框中读取的字符串类型CString必须转换成为WCHAR型,如下(m_strText为文本框变量):
CString strS
m_strText.GetWindowText(strSpeak);
wChar[256];
memset(wChar ,0,256);
MultiByteToWideChar( CP_ACP , 0 , strSpeak , strSpeak.GetLength() , wChar , 256);
这样就将文本框中的字符串strSpeak转化为WCHAR型的wChar变量中了.
开始朗读的代码:
hr =m_cpVoice-&Speak( wChar, SPF_ASYNC |SPF_IS_NOT_XML, 0 );
如果要解读一个XML文本,用:
hr =m_cpVoice-&Speak( wChar, SPF_ASYNC |SPF_IS_XML, 0 );
暂停的代码:
m_cpVoice-&Pause();
恢复的代码:
m_cpVoice-&Resume();
结束的代码:(上面的例子中已经给出了)
hr =m_cpVoice-&Speak( NULL, SPF_PURGEBEFORESPEAK,0);
跳过部分朗读的文字
在朗读的过程中,可以跳过部分文字继续后面的朗读,代码如下:
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&ISpStream&
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文件
TCHARszFileName[256];//假设这里面保存着目标文件的路径
USES_CONVERSION;
WCHAR m_szWFileName[MAX_FILE_PATH];
wcscpy( m_szWFileName,T2W(szFileName) );//转换成宽字符串
//创建一个输出流,绑定到wav文件
CSpStreamFormat
CComPtr&ISpStream& cpWavS
CComPtr&ISpStreamFormat&
HRESULT hr =m_cpVoice-&GetOutputStream(&cpOldStream );
if (hr == S_OK) hr =OriginalFmt.AssignFormat(cpOldStream);
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( wChar, 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
9 设置SAPI通知消息。
SAPI在朗读的过程中,会给指定窗口发送消息,窗口收到消息后,可以主动获取SAPI的事件,根据事件的不同,用户可以得到当前SAPI的一些信息,比如正在朗读的单词的位置,当前的朗读口型值(用于显示动画口型,中文语音的情况下并不提供这个事件)等等。要获取SAPI的通知,首先要注册一个消息:
m_cpVoice-&SetNotifyWindowMessage( hWnd,WM_TTSAPPCUSTOMEVENT, 0, 0 );
这个代码一般是在主窗口初始化的时候调用,hWnd是主窗口(或者接收消息的窗口)句柄。WM_TTSAPPCUSTOMEVENT是用户自定义消息。在窗口响应WM_TTSAPPCUSTOMEVENT消息的函数中,通过如下代码获取sapi的通知事件:
// 使用这个类,比用 SPEVENT结构更方便
while(event.GetFrom(m_cpVoice) == S_OK )
switch( event.eEventId )
eEventID有很多种,比如SPEI_START_INPUT_STREAM表示开始朗读,SPEI_END_INPUT_STREAM表示朗读结束等。
可以根据需要进行判断使用。
7:语音识别
利用微软Speech SDK 5.1在MFC中进行语音识别开发时的主要步骤,以Speech API 5.1+VC6为例:
1、初始化COM端口
一般在CWinApp的子类中,调用CoInitializeEx函数进行COM初始化,代码如下:
::CoInitializeEx(NULL,COINIT_APARTMENTTHREADED); // 初始化COM
注意:调用这个函数时,要在工程设置(project settings)-&C/C++标签,Category中选Preprocessor,在Preprocessor definitions:下的文本框中加上“,_WIN32_DCOM”。否则编译不能通过。
2、创建识别引擎
微软Speech SDK 5.1 支持两种模式的:共享(Share)和独享(InProc)。一般情况下可以使用共享型,大的服务型程序使用InProc。如下:
hr = m_cpRecognizer.CoCreateInstance(CLSID_SpSharedRecognizer);//Share
hr = m_cpRecognizer.CoCreateInstance(CLSID_SpInprocRecognizer);//InProc
如果是Share型,可直接进到步骤3;如果是InProc型,必须使用 ISpRecognizer::SetInput 设置语音输入。如下:
CComPtr&ISpObjectToken& cpAudioT
//定义一个token
hr = SpGetDefaultTokenFromCategoryId(SPCAT_AUDIOIN, &cpAudioToken); //建立默认的音频输入对象
if (SUCCEEDED(hr)) { hr = m_cpRecognizer-&SetInput(cpAudioToken, TRUE);}
CComPtr&ISpAudio& cpA
//定义一个音频对象
hr = SpCreateDefaultObjectFromCategoryId(SPCAT_AUDIOIN, &cpAudio);//建立默认的音频输入对象
hr = m_cpRecoEngine-&SetInput(cpAudio, TRUE);//设置识别引擎输入源
3、创建识别上下文接口
调用 ISpRecognizer::CreateRecoContext 创建识别上下文接口(ISpRecoContext),如下:
hr = m_cpRecoEngine-&CreateRecoContext( &m_cpRecoCtxt );
4、设置识别消息
调用 SetNotifyWindowMessage 告诉Windows哪个是我们的识别消息,需要进行处理。如下:
hr = m_cpRecoCtxt-&SetNotifyWindowMessage(m_hWnd, WM_RECOEVENT, 0, 0);
SetNotifyWindowMessage 定义在 ISpNotifySource 中。
5、设置我们感兴趣的事件
其中最重要的事件是”SPEI_RECOGNITION“。参照 SPEVENTENUM。代码如下:
const ULONGLONG ullInterest = SPFEI(SPEI_SOUND_START) | SPFEI(SPEI_SOUND_END) | SPFEI(SPEI_RECOGNITION) ;
hr = m_cpRecoCtxt-&SetInterest(ullInterest, ullInterest);
6、创建语法规则
语法规则是识别的灵魂,必须要设置。分为两种,一种是听说式(dictation),一种是命令式(command and control---C&C)。首先 利用ISpRecoContext::CreateGrammar 创建语法对象,然后加载不同的语法规则,如下:
//dictation
hr = m_cpRecoCtxt-&CreateGrammar( GIDDICTATION, &m_cpDictationGrammar );
(SUCCEEDED(hr))
hr = m_cpDictationGrammar-&LoadDictation(NULL, SPLO_STATIC);//加载词典
hr = m_cpRecoCtxt-&CreateGrammar( GIDCMDCTRL, &m_cpCmdGrammar);
然后利用ISpRecoGrammar::LoadCmdxxx 加载语法,例如从CmdCtrl.xml中加载:
WCHAR wszXMLFile[20]=L"";
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)"CmdCtrl.xml"
, -1, wszXMLFile, 256);//ANSI转UNINCODE
hr = m_cpCmdGrammar-&LoadCmdFromFile(wszXMLFile,SPLO_DYNAMIC);
注意:C&C时,语法文件使用xml格式,参见Speech SDK 5.1 中的 Designing Grammar Rules。简单例子:
&GRAMMAR LANGID="804"&
&ID NAME="CMD" VAL="10"/&
&RULE NAME="COMMAND" ID="CMD" TOPLEVEL="ACTIVE"&
&p&尹成&/P&
&p&山东大学&/p&
&p&中科院&/p&
&/GRAMMAR&
LANGI*="804"代表简体中文,在&*&...&/*&中增加命令。
7、在开始识别时,激活语法进行识别
hr = m_cpDictationGrammar-&SetDictationState( SPRS_ACTIVE );//dictation
hr = m_cpCmdGrammar-&SetRuleState( NULL,NULL,SPRS_ACTIVE );//C&C
8、获取识别消息,进行处理
截获识别消息(WM_RECOEVENT),然后处理。识别的结果放在CSpEvent的ISpRecoResult 中。如下:
USES_CONVERSION;
switch (event.eEventId)
case SPEI_RECOGNITION:
//识别出了语音输入
m_bGotReco = TRUE;
static const WCHAR wszUnrecognized[] = L"&Unrecognized&";
CSpDynamicString dstrT
//取得识别结果
if (FAILED(event.RecoResult()-&GetText(SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, TRUE ,&dstrText, NULL)))
dstrText = wszU
dstrText.CopyToBSTR(&SRout);
Recstring.Empty();
Recstring = SR
//进一步处理
9、释放创建的引擎、识别上下文对象、语法等。调用相应的Release函数即可。
&&&&推荐文章:
【上篇】【下篇】

我要回帖

更多关于 speechsdk5.1语音库 的文章

 

随机推荐