语音库保存着常用汉字的发音(多音的汉字只记录其一种发音,这也是本系统的一个缺陷,需要以后完善),所以先要得到一汉字集,这个汉字集包含了大部分常用的汉字,然后在根据这个汉字集,来一个个的取得汉字的发音,并且按一定的规则保存到语音库中。
所以实现语音库可以分为三步:
1.1:取得常用汉字的集合
1.2:根据汉字集,使用一些朗读软件生成该汉字集的语音文件
1.3:处理汉字集语音文件的格式,使它能符合我们的要求
1.1根据汉字编码规则获取汉字字符集的文本文件
1.1.1编码知识:
所谓编码,是以固定的顺序排列字符,并以此作为记录、存贮、传递、交换的统一内部特征。一个汉字有ASC II码、区位码等与之对应。ASC II码中对应于码值161到254的字符用于表示汉字,每个汉字用两个ASC1I码值对应的字符表示。区位码用4位数字表示,前两位从01到94称区码,后两位从01到94称位码。一个汉字的前一半是ASC II码为“160+区码”的字符,后一半是ASCII码为“160+位码”的字符。例如:“刘”的区位码是3385,其意为区码33和位码85,它是由ASCII码为160+33=193和160+85=245的两个字符组成。
该文所说的汉字字符集一般是指ISO 10646.1 即GB130o0.1。在Windows 95/98/2000中,微软提供了“汉字扩展内码规范(GBK)”以解决汉字的收字不足、简繁共存、简化代码体系间转换等汉字信息交换的瓶颈问题,利用GBK可以方便解决“镕”、“薯”等大量汉字的交换问题而不必自行造字了。GBK字库共分为5部分,其中GBK/1和GBK/5为符号部分,GBK/2为国标汉字部分,GBK/3和GBK/4为扩展汉字部分。其中,第16区至55区为一级汉字,以拼音排序,共计3755字,56—87区为二级汉字,按偏旁部首排序,共计3008字。
1.1.2程序代码:
用C++实现的获取GBK中一级汉字字符的代码段如下:
//getgbk.cpp一获取GBK汉字码文件
#include <strstream h> //字符串I/O操作
#include <fstream.h> //文件I/O操作
unsigned char oneline[4];
ofstream ofs("gbhz.txt",ios::binary );
oneline[2]=163;
oneline[3]=172;
int qm;
int wm;
for( qm=176;qm<=247;qm++) //区码0XB0—0XD7 87
for(wm=161;wm<=254;wm++) //位码0XA1—0XFE
if(!((qm==247)&&(wm=250)))
//剔除GBK中没有编码的字位
{
oneline[0]=qm; //汉字区码
oneline[1]=wm; //汉字位码
ofs.write((char *)&oneline,4); //写一行至gbhz txt
}//if end
1.2:根据取得的汉字集,使用一些报读软件生成该汉字集的语音文件,一般为WAV格式的。
这里取名为gbhz.wav
1.3:处理汉字集读音文件的格式
1.3.1 理解WAV文件格式
WAVE文件作为多媒体中使用的声波文件格式之一.它是以RlFF格式为标准的 每个wAVE文件的头4个字节便是
“RIFF” WAVE文件由文件头和数据体两大部分组成。其中文件头又分为RIFE/WAV文件标识段和声音数据格式说明段两部分 。WAVE文件格式说明见下表,
内容
数据类型
字节数
“RIFF”标志
Char
4Byte
文件大小
Long int
4Byte
“wave”标志
Char
4Byte
“fmt”标志
Char
4Byte
PCMWAVFORMAT数据结构大小
Long int
4Byte
PCMWAVFORMAT数据结构
“data”标志
Char
4Byte
语音数据大小
Long int
4Byte
可以以时域-幅度的方式显示出原始声音的波形,这是最简单同时也是最直接的信息处理方式。在时域范国内,可以观察该信号波形是否连续,中间是否有祧变等。据发现,经语音引擎处理后,每个汉字所对应的语音数据长度不尽相同.这给以后截取每个汉字的语音数据造成了困难。因此.为了区分每个汉宇的语音数据.在生成汉字字符集时.在每个汉字后添加了一个逗号作为闻隔符 这样生成的语音文件的波形图(部分)如图1所示
图1:
1.3.2生成新的汉字字符集的话音文件的算法
(1)打开gbhz.wav.从gbhz.wav中读人文件头CHAR HEAD[46]。46为文件头长度。
(2)从gbhz.wav 中读入第一个汉字的语音波形数据放入CHAR BUFFER[3200]。
3200为一个汉字的语音波形数据长度.选取决于形成语音的速率、音质等因素
(3)读后面的数据,如果是0x80则不做处理.继续往后读。
ox8O是逗号语音数据,既没有发音的数据。
(4)直到读到第一个不是逗号的语音数据,开始记录第二
个汉字的语音波形数据到CHAR BUFFER[3200]
(5)重复3.4步直到读完全部的语音波形数据。
合成新的汉字字符集的语音文件后的波形图(部分)如图2所示
图2:新的语音文件波形图
1.3.3 处理代码如下:
#define MAXLEN 32000
int HandingWav()
{
FILE * fpf,*fpt; //文件操作指针
if((fpf=fopen("gbhz.wav","rb+"))==NULL) //gbhz.wav为处理前的语音文件
return -1;
if((fpt=fopen("ddd.wav","rb+"))==NULL) //ddd.wav为合成的新的语音文件
return -1;
char head[46]; //wav文件的文件头长度
char data[100]; //用来加速文件处理的数组
char buffer[MAXLEN];
memset(buffer,0,MAXLEN);
fread(head,sizeof(head),1,fpf); //head of wav
fwrite(head,sizeof(head),1,fpt);
while(!feof(fpf))
{
fread(buffer,MAXLEN,1,fpf); //读一个字的发音
fwrite(buffer,MAXLEN,1,fpt); //写一个字
memset(buffer,0,MAXLEN);
fread(data,1,1,fpf); //读一个字节
while(data[0]==char(0x80)) //判断是否为0x80
{
if(fread(data,100,1,fpf)==-1) //每次取100个字节,但只判断第一个字节,这样可以加速文件处理
{
fclose(fpf);
fclose(fpt);
return -1;
} //end if
} //end while 判断是否为0x80
} // end while(!feof(fpf))
fclose(fpf); //关闭文件
fclose(fpt);
return 0;
}
经过以上的操作后,就形成了我们所要的语音库了。
文章导读:
中文TTS 的简单实现(基于linux)之 前言
http://blog.csdn.net/dedodong/archive/2006/07/15/923543.aspx
中文TTS 的简单实现(基于linux)之 实现原理:
http://blog.csdn.net/dedodong/archive/2006/07/16/927041.aspx
中文TTS 的简单实现(基于linux)之 实现语音合成
http://blog.csdn.net/dedodong/archive/2006/08/22/1105879.aspx
中文TTS 的简单实现(基于linux)之 后记
http://blog.csdn.net/dedodong/archive/2006/08/24/1109908.aspx
注:我实现该系统主要是参考
袁嵩的《一个TTS系统的实现方案》—— 计算机工程与应用2004.21 229
而且我写的东西,大部分是来自他的论文,在此我要向他表示忠心的谢意。