科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件中文TTS 的简单实现(基于linux)之 实现语音合成

中文TTS 的简单实现(基于linux)之 实现语音合成

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

合成语音归根到底是根据汉字在字符集的定位来取语音库中的数据。

作者:冬瓜 来源:CSDN 2008年3月26日

关键字: 语音合成 TTS 中文 开源

  • 评论
  • 分享微博
  • 分享邮件

合成语音归根到底是根据汉字在字符集的定位来取语音库中的数据

定位方法:

根据救字的两个字节中的值.从高字节算出汉字的位wm.从低字节算出汉字的区qm,
(qm一176)*94+wm一160就是该况字在汉字集里的位置position,
而该汉字所对应的语音数据的偏移量就是(position一1) 3200+46。

根据定位方法取得汉字在语音库中的发音数据后,根据WAV格式合成语音文件。

定位和合成代码如下:
#define MAXLEN  32000

/*
参数str:为纯汉字的字符串,且编码格式为GBK
返回值:
-1:表示语音库文件打开错误
-2:表示合成语音文件 打开/生成错误
其它:函数执行成功
*/
int   wav(char *str) 
{
 FILE * fpf,*fpt;   //文件指针
 int qm,wm;      //汉字区、位码
 int re;               //函数返回值
 long fileleng=0;   //文件长度 后面修改WAV格式时有用  
 if((fpf=fopen("ddd.wav","rb+"))==NULL)    //打开语音库文件
  return -1;
 
 if((fpt=fopen("china.wav","wb+"))==NULL)  //打开或生成合成后的语音文件,用来播放的
  return -2;

 char head[46];                       //WAV 文件头
 char buffer[MAXLEN];           //发音数据BUFF
 memset(buffer,0,MAXLEN);  //置0
 
 fread(head,sizeof(head),1,fpf);     //读语音库文件头
 fwrite(head,sizeof(head),1,fpt);    //写入合成语音文件
 
 int l=strlen(str);
 char *s=str;
 for(int i=0;i<=l;i=i+2)
 {
  qm=(unsigned char)*(s+i);      //取汉字的区码
  wm=(unsigned char)*(s+1+i);      //取汉字的位码

  if (qm<176||qm>215)   //判断是否在汉字字符集中         
   continue;
    
  if (wm<161||wm>254)  //判断是否在汉字字符集中
    continue;
  

  int position =(qm-176)*94+wm-160;        
  int offset=(position-1)*MAXLEN+46;     //定位
  fseek(fpf,offset,0);
  fread(buffer,sizeof(buffer),1,fpf);     //取发音数据
  fwrite(buffer,sizeof(buffer),1,fpt);     //写入合成文件
  fileleng++;                                          //合成文件长度增加
  
 }   //end for
 
 re =fileleng;
 fileleng=fileleng*MAXLEN;
 fseek(fpt,42,SEEK_SET);
 fwrite(&fileleng,sizeof(long),1,fpt);    //修改合成文件的WAV格式,主要是修改文件大小,具体请看WAV格式表
 
 fileleng+=44;
 
 fseek(fpt,4,SEEK_SET);
 fwrite(&fileleng,sizeof(long),1,fpt);  //修改合成文件的WAV格式,主要是修改文件大小,具体请看WAV格式表
 
 fclose(fpf);     //关闭文件
 fclose(fpt);
 return re;
}

 

其它:
由函数WAV可以看出,我们接收用户的输入字符的编码必须为GBK编码,
所以如果系统使用的不是 GBK编码的话,我们还应当进行编码转换。
如果编码正确的话,还得从把用户的输入中把中文字符给提取出来。
为此,我写了小段代码,用来过滤非中文字符的。
void trans(char *str)
{
 int i = 0, j = 0;
 while( str[i] != '\0' )
 {
  if ( str[i] < 0 )
      {
   str[j++] = str[i++];
   str[j++] = str[i++];
     }
  else
     i++;
 
 }   //end while
 str[j] = '\0'; 
}

 

 

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章