
SAPI,软件中的语音技术包括两方面的内容,一个局必若春效其联脚果是语音识别(speech recognition来自) 和语音合成绿茶待策(speech synthesis)。这两个技术都需要语音引擎的支持。
- 外文名称 The Microsoft Speech API.
- 语音技术 语音识别,语音合成
- 简称 SAPI
- 发布日期 8/8/2001
软件介绍
软件中的语音技术包括两方面的内容,一个是语音识别(speech recognition) 和语音合成(speech synthesis)。这两个技术都需要语音引擎的支持。微软推出的应用编程接口API,虽然不是业界标准,但是应来自用比较广泛。
SAPI全称 The Microsoft Speech API.相关的SR和SS引擎位于Speech SDK开发包中。这个语音引擎支持多种语言的识别和朗读,包括英文、中文、日文等。
SAPI包括以下组件对象(接口):
(1)Voice Commands API。对应用程序进行控制,一般特满查宁用于语音识别系统中。识别某个命令后,会调用相关接口是应用程序完成对应的功能。如果程序想实现语音控适眼宪制,必须使用此组360百科对象。
(2)Voice Dictation API。听写输入,即语音识别接口。
(3)Voice Text API。完成从文字到语音的转换,即语音合成。
(4)Voice Telepho指用考ne API。语音识别和语音合成综合运用元切断真破渐对式测名到电话系统之上,利用此接口可以建立一个电话夜革露硫元低延措应答系统,甚至可以通过电话控制计算机。
(5)Audio Objects API。封装了计算机发音系统年可纸四溶配利。
SAPI是架构在COM基础上的,微软还提供了ActiveX控件,所以不仅行耐余某振治在曾光肥随可用于一般的windows程序,还可以用于网页、VBA甚至EXCEL的图表中。如果对COM感到陌生,还可以使用微软的C++ WRAPP展调话械小机附杨跳ERS,它用C++类封苏请体第业学才余般练装了语音SDK COM权春分片对象。
软件安装
首先下载开发包
Microsoft Speech SDK 5.1添加了Automation支持。所以可以在VB,ECMAScript等支持Automation的语言中使用。
版本说明:
Version: 5.1
语音: 利娘齐English
下载尺寸: 2.0 MB - 288.8 MB
这个SDK开发包还包括了可以随岩加曾选便发布的英文和中文的语音合成引擎(TTS),和英文、中文、日文的语音识别引擎(SR队主之所底继率督员械处)。
系统要求9害境永书因市当亚渐8以上版本。编译开发包中的例子程序需要vc6以上环境。
******下载说明******:
(1)如果要下载例子程序,说明文档,SAPI以及用于开发的美国英语语音引擎,需要下载SpeechSD正K51.exe,大约68M。
(2)如果想要使用简体中文和日文的语音引擎,需要下载SpeechSDK51LangPack.exe。大约82M。
(3)如果想要和自己的软件一起发布语音引擎,需要下载SpeechSDK51MSM.exe,大约132M。
(在这个地址,我未能成功下载)。
(4)如果要获取XP下的 Mike 和 Mary 语音,下载Sp5好境免校吃类TTIntXP.exe。若构改可语英白及核伤大约3.5M。
(5实接次尽九度件或力殖)如果要获取开发包的文档说明,请下载sapi.chm。大约2.3M。这个在sdk51里面已经包含。
下载完毕后,首先安装SpeechSDK51.exe,然后安装中文语言补丁包SpeechSDK51LangPack,然后展开
msttss22l,自动将所需dll安装到系统目录。
配置环境
提三到缩急究在vc6.0的环境下编译语音工程,首先要配置编译环境。来自假设sdk安装在d:\Microsoft Speech SDK 5.1\路径下,打开工程设置对话框,在c/c++栏中选择Preprocessor分类,然后在"附加包含路径"中输入
d:\Microsoft Speech SDK 5.1\include
告诉vc编译程序所需的SAPI头文件的位置。
然后360百科切换到LINK栏,在Input分类下的附加库路径中输入:
d:\Microsoft Speech 先盟零呼功威刘帮怀SDK 5.1\lib\i386
九与够三运革脚完始 使vc在链接的时候能够找到sapi.lib。
程序前志应用
即使用SAPI实现TTS(Text to Speech)。
首先要初始化语音接口
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;
HRESU洲通LT hr = m_cpVoice.CoCreateInstance( CLSID_S状南移居参烈pVoice );
在下面的例子中都用这个m_cpVoice变量。
CLSID_SpVoice的定义位绿加犯于SPAI.H中。
获取/设置输出频率
SAPI朗读文字的时候,可以采用多种频率方式输出声音,比如:
8kHz 8Bit Mono、8kHz 8Bit Stereo、44kHz 真旧美几乙16Bit Mon跟硫置顾镇o、44kHz 16Bit Stereo等。在音兴沿河余南零治调上有所差别。具体可以参考sapi.h。
可以使用如下代孔史脱码获取当前的配置:
CComPtr cpStre温电齐都机演保法走留am;
HRESULT hrOutputStrea轴负信待节青念位双m = m_cpVoice->GetOutputStream(&cpStream);
if (hrOutputStream == S_OK)
{
CSp冷听饭话故StreamForma伯防科席应紧盟钟采岩附t Fmt;
hr = Fmt.AssignFormat(cpStream);
if (SUCCEEDED(hr))
{
SPSTREAMFORMAT eFmt = Fmt.ComputeFormatEnum();
}
}
SPSTREAMFORMAT 是一个ENUM类型,定义位于SPAI.H中。每一个值对应了不同的频率设置。例如 SPSF_8kHz8BitStereo = 5
3 通过如下代码设置当前朗读频率
:
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 );
}
获取/设置播放所用语音
引擎中所用的语音数据文件一般保存在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);
开始/暂停/恢复/结束当前的朗读
要朗读的文字必须位于宽字符串中,假设位于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);
跳过部分朗读的文字
在朗读的过程中,可以跳过部分文字继续后面的朗读,代码如下:
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文件
}
将朗读的结果保存到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 );
设置朗读音量和速度
m_cpVoice->SetVolume((USHORT)hpos); //设置音量,范围是 0 - 100
m_cpVoice->SetRate(hpos); //设置速度,范围是 -10 - 10
hpos的值一般位于
设置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表示朗读结束等。
可以根据需要进行判断使用。
C#调用SAPI实现语音合成的两种方法:
我们都知道现在的语音合成TTS是可以通过微软的SAPI实现的,好处我就不多说了,方便而已,因为在微软的操作系统里面就自带了这个玩意,主要的方式有两种:
1、使用COM组件技术,不管是C++,C#,Delphi都能玩的转,开发出来的东西在XP和WIN7都能跑。(要引入SpeechLib,好像在项目上点引用,然后选到系统COM吧,好久没弄,记不清楚了)
2、使用WIN7的windows api,其实最终还是调用了SAPI,所以开发出来的东西就只能在WIN7上面跑。
其实不管是哪一种,都是调用SAPI,可能后一种代码比较简单,使用已经安装的TTS引擎,现在一般用NeoSpeech,这个就不解释了,太强大了这个发音。。。
COM组件技术:
C#代码
public class Speach
{
private static Speach _Instance = null ;
private SpeechLib.SpVoiceClass voice =null; //SAPI5.1
private SpeechLib.SpVoice voice = null;//SAPI 5.4
private Speach()
{
BuildSpeach() ;
}
public static Speach instance()
{
if (_Instance == null)
_Instance = new Speach() ;
return _Instance ;
}
private void SetChinaVoice()
{
voice.Voice = voice.GetVoices(string.Empty,string.Empty).Item(0) ;
}
private void SetEnglishVoice()
{
voice.Voice = voice.GetVoices(string.Empty,string.Empty).Item(1) ;
}
private void SpeakChina(string strSpeak)
{
SetChinaVoice() ;
Speak(strSpeak) ;
}
private void SpeakEnglishi(string strSpeak)
{
SetEnglishVoice() ;
Speak(strSpeak) ;
}
public void AnalyseSpeak(string strSpeak)
{
int iCbeg = 0 ;
int iEbeg = 0 ;
bool IsChina = true ;
for(int i=0;i<strSpeak.Length;i++)
{
char chr = strSpeak[i] ;
if (IsChina)
{
if (chr<=122&&chr>=65)
{
int iLen = i - iCbeg ;
string strValue = strSpeak.Substring(iCbeg,iLen) ;
SpeakChina(strValue) ;
iEbeg = i ;
IsChina = false ;
}
}
else
{
if (chr>122||chr<65)
{
int iLen = i - iEbeg ;
string strValue = strSpeak.Substring(iEbeg,iLen) ;
this.SpeakEnglishi(strValue) ;
iCbeg = i ;
IsChina = true ;
}
}
}//end for
if (IsChina)
{
int iLen = strSpeak.Length - iCbeg ;
string strValue = strSpeak.Substring(iCbeg,iLen) ;
SpeakChina(strValue) ;
}
else
{
int iLen = strSpeak.Length - iEbeg ;
string strValue = strSpeak.Substring(iEbeg,iLen) ;
SpeakEnglishi(strValue) ;
}
}
private void BuildSpeach()
{
if (voice == null)
voice = new SpVoiceClass() ;
}
public int Volume
{
get
{
return voice.Volume ;
}
set
{
voice.SetVolume((ushort)(value)) ;
}
}
public int Rate
{
get
{
return voice.Rate ;
}
set
{
voice.SetRate(value) ;
}
}
private void Speak(string strSpeack)
{
try
{
voice.Speak(strSpeack,SpeechVoiceSpeakFlags.SVSFlagsAsync) ;
}
catch(Exception err)
{
throw(new Exception("发生一个错误:"+err.Message)) ;
}
}
public void Stop()
{
voice.Speak(string.Empty,SpeechLib.SpeechVoiceSpeakFlags.SVSFPurgeBeforeSpeak) ;
}
public void Pause()
{
voice.Pause() ;
}
public void Continue()
{
voice.Resume() ;
}
}//end class
在 private SpeechLib.SpVoiceClass voice =null;这里,我们定义个一个用来发音的类,并且在第一次调用该类时,对它用BuildSpeach方法进行了初始化。
我们还定义了两个属性Volume和Rate,能够设置音量和语速。
我们知道,SpVoiceClass 有一个Speak方法,我们发音主要就是给他传递一个字符串,它负责读出该字符串,如下所示。
C#代码
private void Speak(string strSpeack)
{
try
{
voice.Speak(strSpeack,SpeechVoiceSpeakFlags.SVSFlagsAsync) ;
}
catch(Exception err)
{
throw(new Exception("发生一个错误:"+err.Message)) ;
}
}
第二种使用.NET类库和系统API的代码如下:
C#代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Speech.Synthesis;
using System.Speech;
namespace StudyBeta
{
public class SRead
{
public SpeechSynthesizer synth; //语音合成对象
public SRead()
{
synth = new SpeechSynthesizer();
}
public SRead(int m, int n)
{
//使用 synth 设置朗读音量 [范围 0 ~ 100]
synth.Volume = m;
//使用 synth 设置朗读频率 [范围 -10 ~ 10]
synth.Rate = n;
}
public void SpeakChina(string ggg)
{
//SpVoice Voice = new SpVoice();
synth.SelectVoice("Microsoft Lili");
//Voice.Speak(ggg, SpFlags);
synth.SpeakAsync(ggg);
//String speechPeople = synth.Voice;
//使用 synth 设置朗读音量 [范围 0 ~ 100]
// synth.Volume = 80;
//使用 synth 设置朗读频率 [范围 -10 ~ 10]
// synth.Rate = 0;
//使用synth 合成 wav 音频文件:
//synth.SetOutputToWaveFile(string path);
}
public void SpeakEnglish(string ggg)
{
//SpVoice Voice = new SpVoice();
synth.SelectVoice("VW Julie");
synth.Speak(ggg); //ggg为要合成的内容
}
public int m
{
get
{
return synth.Volume;
}
set
{
synth.Volume = value;
}
}
public int n
{
get
{
return synth.Rate;
}
set
{
synth.Rate = value;
}
}
}
结束语
SAPI的功能很多,比如语音识别、使用语法分析等,由于条件和精力有限,我未能一一尝试,感兴趣的朋友可以自己安装一个研究一下。