中文TTS 的简单实现(基于linux)之 语音库的实现
2012-01-11 16:23
465 查看
语音库保存着常用汉字的发音(多音的汉字只记录其一种发音,这也是本系统的一个缺陷,需要以后完善),所以先要得到一汉字集,这个汉字集包含了大部分常用的汉字,然后在根据这个汉字集,来一个个的取得汉字的发音,并且按一定的规则保存到语音库中。
语音库保存着常用汉字的发音(多音的汉字只记录其一种发音,这也是本系统的一个缺陷,需要以后完善),所以先要得到一汉字集,这个汉字集包含了大部分常用的汉字,然后在根据这个汉字集,来一个个的取得汉字的发音,并且按一定的规则保存到语音库中。
所以实现语音库可以分为三步:
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文件格式说明见下表,
可以以时域-幅度的方式显示出原始声音的波形,这是最简单同时也是最直接的信息处理方式。在时域范国内,可以观察该信号波形是否连续,中间是否有祧变等。据发现,经语音引擎处理后,每个汉字所对应的语音数据长度不尽相同.这给以后截取每个汉字的语音数据造成了困难。因此.为了区分每个汉宇的语音数据.在生成汉字字符集时.在每个汉字后添加了一个逗号作为闻隔符 这样生成的语音文件的波形图(部分)如图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;
}
经过以上的操作后,就形成了我们所要的语音库了。
语音库保存着常用汉字的发音(多音的汉字只记录其一种发音,这也是本系统的一个缺陷,需要以后完善),所以先要得到一汉字集,这个汉字集包含了大部分常用的汉字,然后在根据这个汉字集,来一个个的取得汉字的发音,并且按一定的规则保存到语音库中。
所以实现语音库可以分为三步:
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文件格式说明见下表,
[align=center]内容[/align] | [align=center]数据类型[/align] | [align=center]字节数[/align] |
[align=center]“RIFF”标志[/align] | [align=center]Char[/align] | [align=center]4Byte[/align] |
[align=center]文件大小[/align] | [align=center]Long int[/align] | [align=center]4Byte[/align] |
[align=center]“wave”标志[/align] | [align=center]Char[/align] | [align=center]4Byte[/align] |
[align=center]“fmt”标志[/align] | [align=center]Char[/align] | [align=center]4Byte[/align] |
[align=center]PCMWAVFORMAT数据结构大小[/align] | [align=center]Long int[/align] | [align=center]4Byte[/align] |
[align=center]PCMWAVFORMAT数据结构[/align] | [align=center] [/align] | [align=center] [/align] |
[align=center]“data”标志[/align] | [align=center]Char[/align] | [align=center]4Byte[/align] |
[align=center]语音数据大小[/align] | [align=center]Long int[/align] | [align=center]4Byte[/align] |
图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)之 实现语音合成
- TTS5.1语音引擎(中文)实现文本阅读和音频输出为WAV
- 力控调用捷通TTS ActiveX控件实现中文文本转语音
- Linux环境下C语言实现简单的基于文件的学生信息管理系统
- .net实现简单语音朗读(TTS)功能
- android 语音播报(通过手说tts 实现中文语音播报)
- Linux下基于C实现的socket简单文件上传实例
- Android 通过手说tts中文语音包实现中文朗读
- 学习LSM(Linux security module)之四:一个基于LSM的简单沙箱的设计与实现
- android上实现语音识别,基于google的语音识的简单例子.
- Linux下基于C实现的socket简单文件下载实例
- 基于Linux内核的USB鼠标驱动的简单实现
- LABVIEW调用捷通TTS ActiveX控件实现中文文本转语音
- Delphi 实现简易语音发音(基于TTS方式)
- 在Linux下基于tcp协议实现一个简单的通信
- Linux下基于C实现的socket简单文件下载实例 - 科技 - 紫苹果在线 - 快乐生活每一天
- 使用Svox实现中文转语音TextToSpeech(TTS)
- linux网络编程之用socket实现简单客户端和服务端的通信(基于UDP)
- 基于嵌入式linux的freetype矢量字体简单显示的实现
- DMA设备驱动(三)————基于Linux3.4.2的dma设备驱动的简单实现