3个开源TTS(二)eSpeak的简要分析使用

         继续开源TTS分析,只能说是给刚起步的人一点帮助了,毕竟不是专业做这一块的。今天主要先简单介绍TTS过程,然后以eSpeak的动态库编译使用,获得wav文件结束。
         前文介绍eSpeak是c语言写的一个小型的、开放源码的语音合成系统,支持多种语言,这里包括汉语,甚至是粤语,可以看看他的博客和演讲【1】。在eSpeak的介绍里特别强调了采用“formant synthesis”(共振峰)合成方法,因此简单了解下TTS的一般过程,由于并非专业,难免有问题,请指出和谅解。
         TTS(Text to Speech),也称为语音合成技术,一般分为三个步骤,语言学处理,韵律处理和声学处理。语言学处理就是模拟人对自然语言的理解过程,完成文本规范化、分词、语法分析和语义分析,使处理后的输出能够为计算机所理解,并在其中加入所需要的各种发音提示,包括数字、特殊词汇、断句停顿等;韵律处理则是在文本的理解基础上,规划出音段特征,如音高、音长和音强等,利用韵律标记系统语调、节奏和重音这些韵律特征;声学处理则是在前两部分提供的信息基础上,利用语音语料库,统计与规则结合语境参数、声学参数等信息解决韵律的控制语音的合成。简单来说就是模拟人朗读的过程,从一段文本的输入进行分词、理解开始,结合人的朗读规律、发声特点对文本进行标记,作为参数在声音输出时产生模拟人声停顿变化等的过程。eSpeak所说的共振峰合成就是最后的语音产生过程。共振峰合成是最典型的基于声学模型的合成技术,其设计的理论基础是语音产生的源-滤波理论,即来自肺部的气流通过声门后,被看成具有一定谱结构的声源S(f),也是声道滤波器的激励信号,声道被看成足一个线性的滤波器,其转移凼数T(f)是口和鼻处的体积速度U(f)和声源s(f)之比,由于实际上总是存离口腔有一段距离的地方得到语音p(f),因此必须考虑到口腔的辐射效应R(f)。(更加详细的资料,自己查找)而我们需要知道共振峰模型受参数提取的影响很大,参数不准确会导致合成音质下降。而波形拼接的合成声音音质最好,但合成新的声音需要事先录制新的说话人的声音,共振峰合成只需在频域对声音作修改。这样的修改包括;振峰偏移、带宽修改、共振峰强度的修改和频带斜坡的修改。Klatt设计了一个串/并联混合型共振峰合成器,可以设置多到八个共振峰.并有单独的滤波器来模拟鼻腔和气管的共振。对声源可做调整或多种选择,以模拟不同的嗓音。(摘自【2】【3】)
        从上面的描述过程大致可以知道TTS的一般过程,而共振峰合成技术是一种语音合成技术中的参数合成方法,通过模拟人的发声过程,建立发声模型,利用对人的浊音源、元音源等声源发声进行参数提取、估计,供后期的共振模拟使用。可见像我这种非专业人士只是知道会有大量的参数调整控制,看来想对此软件进行发声控制使用可以,修改部分规则、扩充语言、词典需要大量时间,再进一步想要优化流畅度之类的,估计就非常困难了。那么下面咱们说怎么利用其提供的源码包编译Windows上的动态库,并使用,Linux上的应该类似。
        本人的环境是Windows XP,使用VC6.0编译,源码包espeak-1.46.02-source,只为了产生wav格式文件,因此没用到PortAudio库,但是编译命令行版本时是直接可以发声的。编译过程非常简单,在源码包下\platforms\windows\windows_dll文件夹中有!ReadMe.txt文件告知。从源码包根目录下的src文件夹中拷贝源码文件到platforms\windows\windows_dll\src中,但不拷贝speak_lib.h  speech.h  StdAfx.h  stdint.h文件。而该eSpeak产生的windows DLL库提供的接口在speak_lib.h中定义看到。我的编译遇到了'espeak_SetParameter' : redefinition; different linkage的重定义错误,只要在定义出前添加ESPEAK_API即可。

        使用代码如下,也可参看【4】,可以把word换成nihao试试。

  1. #include <stdio.h>  
  2. #include "speak_lib.h"   
  3. #pragma comment(lib, "espeak_lib.lib")  
  4.   
  5. FILE *f_wave = NULL;  
  6.   
  7. void Write4Bytes(FILE *f, int value)  
  8. {//=================================  
  9. // Write 4 bytes to a file, least significant first  
  10.     int ix;  
  11.   
  12.     for(ix=0; ix<4; ix++)  
  13.     {  
  14.         fputc(value & 0xff,f);  
  15.         value = value >> 8;  
  16.     }  
  17. }  
  18.   
  19. static int OpenWaveFile(const char *path, int rate)//rate 22050  
  20. //=================================================  
  21. {  
  22.     // Set the length of 0x7ffff000 for --stdout  
  23.     // This will be changed to the correct length for -w (write to file)  
  24.     static unsigned char wave_hdr[44] = {  
  25.         'R','I','F','F',0x24,0xf0,0xff,0x7f,'W','A','V','E','f','m','t',' ',  
  26.         0x10,0,0,0,1,0,1,0,  9,0x3d,0,0,0x12,0x7a,0,0,  
  27.         2,0,0x10,0,'d','a','t','a',  0x00,0xf0,0xff,0x7f};  
  28.   
  29.     if(path == NULL)  
  30.         return(2);  
  31.   
  32.     if(strcmp(path,"stdout")==0)  
  33.     {  
  34. #ifdef PLATFORM_WINDOWS  
  35. // prevent Windows adding 0x0d before 0x0a bytes  
  36.         _setmode(_fileno(stdout), _O_BINARY);  
  37. #endif  
  38.         f_wave = stdout;  
  39.     }  
  40.     else  
  41.         f_wave = fopen(path,"wb");  
  42.   
  43.     if(f_wave != NULL)  
  44.     {  
  45.         fwrite(wave_hdr,1,24,f_wave);  
  46.         Write4Bytes(f_wave,rate);  
  47.         Write4Bytes(f_wave,rate * 2);  
  48.         fwrite(&wave_hdr[32],1,12,f_wave);  
  49.         return(0);  
  50.     }  
  51.     return(1);  
  52. }   //  end of OpenWaveFile  
  53.   
  54.   
  55. static void CloseWaveFile()  
  56. //=========================  
  57. {  
  58.    unsigned int pos;  
  59.   
  60.    if((f_wave == NULL) || (f_wave == stdout))  
  61.       return;  
  62.   
  63.    fflush(f_wave);  
  64.    pos = ftell(f_wave);  
  65.   
  66.     fseek(f_wave,4,SEEK_SET);  
  67.     Write4Bytes(f_wave,pos - 8);  
  68.   
  69.     fseek(f_wave,40,SEEK_SET);  
  70.     Write4Bytes(f_wave,pos - 44);  
  71.   
  72.   
  73.    fclose(f_wave);  
  74.    f_wave = NULL;  
  75.   
  76. // end of CloseWaveFile  
  77.   
  78. int SynthCallback(short *wav, int numsamples, espeak_EVENT *events)  
  79. {  
  80.     // 你可以根据源码程序里编写这部分代码实现生成语音文件功能  
  81.     int wavDataIndex = 0;  
  82.     if(wav == NULL)  
  83.         return 1;  
  84.     else{  
  85.         while(wav[wavDataIndex+numsamples] != NULL)  
  86.             wavDataIndex++;  
  87.         fwrite(wav,sizeof(short),numsamples,f_wave);  
  88.         return(0);  
  89.     }  
  90. }  
  91.   
  92. void main()   
  93. {  
  94.     // TODO: Add your control notification handler code here  
  95.     char word[] = "hello world!";  
  96.     espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS, 0, ".", 0);  
  97.     espeak_SetSynthCallback(SynthCallback);   // 设置回调函数  
  98.     espeak_SetVoiceByName("en");  
  99.     OpenWaveFile("test.wav",22050);  
  100.     espeak_Synth(word, 100, 0, POS_CHARACTER, 0, espeakCHARS_UTF8, NULL ,NULL);  
  101.     espeak_Synchronize();  
  102.     espeak_Terminate();  
  103.     CloseWaveFile();  
  104. }  

         注意拷贝espeak_lib.dll和espeak_lib.lib,还有就是源码包中的espeak-data要拷贝到应用目录下。其中代码都是从eSpeak的命令行源程序中拷贝出来的,因此有些混乱请谅解。而提供的接口自己查看speak_lib.h中的说明就懂意思了。

         参看:
                【1】开源中国 黄冠能博客http://my.oschina.net/hgneng
                【2】语音合成技术的研究与发展-黄南川等
                【3】共振峰语音合成算法研究和实现-赵亮
                【4】跨平台TTS eSpeak Windows开发 http://www.cnblogs.com/hicjiajia/archive/2011/02/02/1948882.html

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿老高

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值