一, 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/speech/download/sdk51
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).
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<ISpVoice> 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<ISpStreamFormat> 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<ISpAudio> 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<ISpObjectToken> 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<ISpStream> 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<ISpStream> cpWavStream;
- CComPtr<ISpStreamFormat> 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 表示朗读结束等.
可以根据需要进行判断使用.
需要的留下 Email, 我给大家发
来源: http://www.bubuko.com/infodetail-2931029.html