初试Text-to-Speech

Text-to-speech,即文本语音转换

前期准备

首先,跑去微软的窝窝里搜了一会,找到了speech的sdk,马上下了,我下的是5.1,再把语言包也下了,安装完后,记得把sdk的include目录和lib目录添加到vc的Tools/Options/Directories里。记得把sdk的include和lib目录路径放到原来vc的引用路径的前面,即如下:

开始

我们还是先以一段Hello World开始吧!看如下代码,下面代码的功能就是让程序说出一句简单的英文,虽然简单,但却给我们展示了用speech sdk如何做到基本的Text to Speech。

为简单起见,我们新建一个console工程,在工程里包含sapi.lib库,去Project/Setting/Links的Object/Library里加上sapi.lib。因为该sdk都是COM函数,在程序开始时调用CoInitialize(NULL);,在程序结束时调用CoUninitialize();。

#include "stdafx.h"
#include <atlbase.h>
extern CComModule _Module;
#include <atlcom.h>
#include <sapi.h>

int main(int argc, char* argv[])
{
    ISpVoice* pVoice = NULL;

    if (FAILED(::CoInitialize(NULL))) //初始化Com库
        return 0;

     HRESULT hr = ::CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void**)&pVoice);

     if (SUCCEEDED(hr))
     {
          hr = pVoice->Speak(L"Hay man! What are you doing now?", 0, NULL);
          pVoice->Release();
          pVoice = NULL;
     }

     ::CoUninitialize();

      return 0;
}

上面的代码运行后,就会听到一个鬼子说了句“嘿门,我爱悠嘟衣脑”,呵,简单吧?下面我们来看看上面的代码都做了些什么事。

1.程序一开始定义了一个ISpVoice接口对象,该对象能把给定的文本或文件(内容)转换成语音输出

2.然后初始化com库

3.接着通过CoCreateInstance创建ISpVoice的实例,

4.然后程序就通过该接口的Speak方法,说出一句“嘿门,我爱悠嘟衣脑”

5.最后,释放com库

就这几步,呵,相当简单,接着,我们来看看我们最不熟悉的Speak函数吧,它的原形是:

HRESULT Speak(
   const WCHAR   *pwcs,
   DWORD          dwFlags,
   ULONG         *pulStreamNumber
);
pwcs 是需要输出(说)的字符串或文件路径,如果为文件路径的话,那么第二个参数必须是SPF_IS_FILENAME 或SPF_IS_XML ,如果这里指定了SPF_IS_FILENAME,那么文件里只能有普通的文本内容,比如双引号,尖括号等等的符号都不能有,否则就说不出话。而这个文本的后缀名则可以是任意。

dwFlags 指定这个函数的输出的细节,例如可以从文本里读取内容(SPF_IS_FILENAME ),或者从XML里读取内容(SPF_IS_XML),或者以异步的方式(SPF_ASYNC )来说话,或者可以用SPF_PURGEBEFORESPEAK 来停止掉之前的异常调用的Speak。下面是第二个参数的各个flag:

typedef enum SPEAKFLAGS
{
    //--- SpVoice flags
    SPF_DEFAULT,         
    SPF_ASYNC,           
    SPF_PURGEBEFORESPEAK,
    SPF_IS_FILENAME,     
    SPF_IS_XML,          
    SPF_IS_NOT_XML,      
    SPF_PERSIST_XML,     

    //--- Normalizer flags
    SPF_NLP_SPEAK_PUNC,  

    //--- Masks
    SPF_NLP_MASK,       
    SPF_VOICE_MASK,     
    SPF_UNUSED_FLAGS    
} SPEAKFLAGS;
详细的请去看sdk自带的帮助。

pulStreamNumber 表示该次调用的一个序号,当该次调用完成后,Speak将会返回这个序号,在本例里用不上,用于事件处理。

这个hello world基本上就这些东西,下面我们通过一个程序,演示一吓第二参数的

#include "stdafx.h"
#include <atlbase.h>
extern CComModule _Module;
#include <atlcom.h>
#include <sapi.h>

int main(int argc, char* argv[])
{
 ISpVoice* pVoice = NULL;

 if (FAILED(::CoInitialize(NULL)))
  return 0;

 HRESULT hr = ::CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void**)&pVoice);

 if (SUCCEEDED(hr))
 {
  //c盘下面必须有test.txt,里面有一些内容(英文),但不能包含"<",">"这些字符
  hr = pVoice->Speak(L"c://test.txt", SPF_IS_FILENAME, NULL);
  ::Sleep(500);
  
  //程序将会说Hello
  hr = pVoice->Speak(L"<Title><T>Hello</T><T>World</T></Title>", SPF_IS_XML, NULL);
  ::Sleep(500);
  
  //SPF_IS_NOT_XML这个标记可以把"<", ">"这个符号不解释成XML,而直接读出,可以联合SPF_IS_FILENAME联合使用,能过|运算
  hr = pVoice->Speak(L"<Hello", SPF_IS_NOT_XML, NULL);
  ::Sleep(500);
  
  //异步调用,可以联合SPF_IS_FILENAME联合使用,能过|运算
  hr = pVoice->Speak(L"If you work hard, you will get more money!", SPF_ASYNC, NULL);
  ::Sleep(500);
  
  //把前面那句话清除掉,即停止前面那句话的输出后,再输出指定内容,可以与SPF_IS_FILENAME联合使用,能过|运算
  hr = pVoice->Speak(L"I love you", SPF_PURGEBEFORESPEAK, NULL);
  ::Sleep(500);
  
  //把标点符号也输出,可以联合SPF_IS_FILENAME联合使用,能过|运算
  hr = pVoice->Speak(L"We are the world!", SPF_NLP_SPEAK_PUNC , NULL);

  pVoice->Release();
  pVoice = NULL;
 }

 ::CoUninitialize();


 return 0;
}

好了,就说到这里吧!英文好的朋友可以通过学习sdk自带的帮助文档来学习,里面比讲的我这里介绍的更详细!呵呵!

虽然Speech sdk很强大,但是通过它读出来的英文,对于一些刚学习英语的朋友还是不那么易懂。。所以,我写了的那个小工具其实没派上用场。。。唉,最后还是决定通过人手录制语单来代替自动发音,不过我想speech sdk应该可以调节读音的快慢和音调的,再研究看看吧!

阅读更多
换一批

没有更多推荐了,返回首页