收藏 分享(赏)

基于汉字编码特性构造函数实现的语音系统.doc

上传人:Facebook 文档编号:7648689 上传时间:2019-05-23 格式:DOC 页数:8 大小:56.50KB
下载 相关 举报
基于汉字编码特性构造函数实现的语音系统.doc_第1页
第1页 / 共8页
基于汉字编码特性构造函数实现的语音系统.doc_第2页
第2页 / 共8页
基于汉字编码特性构造函数实现的语音系统.doc_第3页
第3页 / 共8页
基于汉字编码特性构造函数实现的语音系统.doc_第4页
第4页 / 共8页
基于汉字编码特性构造函数实现的语音系统.doc_第5页
第5页 / 共8页
点击查看更多>>
资源描述

1、基于汉字编码特性构造函数实现的语音系统日期:2006-05-22 来源: 作者: 字体:大 中 小 黄红伟 汤兴华摘要:基于汉字编码的特性,本文构造了一个函数,利用这个函数为每一个汉字建立索引,基于此建立了一个很小的基于文件形式的语音库;在程序里调用这个语音库来达到汉字语音输出的功能。同时给出了实现此语音库的方法、步骤及怎样调用此语音库,及相应的程序代码。关键词:汉字编码, 函数, 语音系统 1关于汉字映射序号函数 f 首先让我们来考查一下汉字的编码(我们这里只研究中国大陆的简体中文 GB2312,下同)。根据 ANSI 字符集 1,每一个汉字由两个字节构成,由一个十六进制数唯一表示,例如:黄

2、,其十六进制编码为:0xBBC6。我们可以看到在构成黄字的这两个字节中,第一个字节表示的值为:0xBB,第二个字节为: 0xC6。通过研究 Windows 系统中的字符映射表,我们可以得出以下命题:命题一 在表示简体中文的这两个字节中,第一个字节的标识范围为:0xB00xF7 ,第二个字节的标识范围为:0xA10xFE。根据命题一,我们得出:命题二 用这两个字节在此标识范围之内所能表示的汉字个数为:(0xF70xB0 1)(0xFE0xA1 1)72946768。事实上字符映射表里的简体汉字个数也为 6768 个。于是我们定义了一个函数 f:定义一: P = f(Q) = (Q1 0xB0)

3、94 + (Q2 0xA1)其中:Q 为任一汉字的十六进制编码,显然这里 Q 由两个字节构成, Q1 为构成 Q 的两个字节中的第一个字节,Q2 为第二个字节,所以:0xB0Q10xF7, 0xA1Q20xFEP = p | 0 t 6767 且 p 为整数不难证明此函数是可逆的。得出命题三:命题三 任意给定一汉字,根据函数 f,总可以找到一个唯一的数 p (pP),让 p来代表这个汉字,反之,给定一数 p(pP),也总有一个唯一的汉字与之对应。于是我们有 f(啊) = f(0xB0A1) = 0,又如 f(黄) = f(0xBBC6) = 1071,如此等等。2.关于汉字拼音对照表通过考查新

4、华字典 2,我们为字符映射表里的 6768 个简体汉字建立了一个汉语拼音对照表。对照表里在每一个拼音的后面加上一个数字,以示声调,第一声用1表示,第二声用2 ,第三声用3 , 第四声用 4,平声用5。例如:啊 a1,黄huang2。由于汉字里同音字比较多,所以 6768 个汉字拼音里实际上不同声调者只有 1317 个。 为了准备基本的语音素材,我们特地从电视台请了一位播音员录制了这 1317 个不同的发音。其中每一个发音是一个单独的 wav 型文件,其文件名就是其对应的汉语拼音,例如huang2这个音的文件名字就是huang2。3.关于语音库的建立语音库分为两个部份,第一部份存贮 6768 个

5、汉字中的每一个汉字语音数据在库中的位置索引,第二部份存贮的是已经录制好的 1317 个 wav 文件的数据。事实上在库中并没有存贮汉字本身,因为根据前面得到的命题三,字符映射表里的每一个汉字都有一个唯一的数字与其对应,故这个语音库的第一部份,也就是索引部份,可以看作一个有 6768 个元素的数组-SndIdx6768 (实际我们后面编程时也是这样实现的),例如 SndIdx1071的值是黄这个字的语音数据在库中的位置偏移量。因为 1317 个语音数据有 10M 之多,故索引数组的元素值远超过 32767,为此数组的每一个元素占有 4 个字节的空间。因此索引大小为:4 6768 25K 。调用语

6、音库时利用了两个缓冲区 :DWORD SndIdx6768 与 char *m_szSndBuff,m_szSndBuff = (char *)malloc( 语音库文件第二部份的大小 )。然后把语音库文件 fsnd.dat 的第一部份,也就是索引部份读入 SndIdx;把第二部份,也就是语音数据部份读入 m_szSndBuff。当需要语音输出的时候,例如要输出 黄的读音,程序根据定义一的函数 f 得出与该字对应的的编号为 1071,于是得到该字的读音在缓冲区 m_szSndBuff 中的偏移量为 SndIdx1071,然后调用 Windows 的用于播放 wav 数据的 API 函数来输出该

7、字的发音:sndPlaySound( m_szSndBuff + SndIdx1071 , SND_SYNC | SND_MEMORY )3。4.关于系统的实现首先我们来研究怎样生成这个语音系统。语音系统的主体部份其实只是一个数据文件 fsnd.dat,我们要做的只是怎样把这些索引与单个的汉字语音数据写到 fsnd.dat 中。对于 6768 个汉字的索引以及 1317 个不同的语音数据文件,我们构造了一个算法做这件事。为此我们设计了一个临时数据库(sndDB),这个库里只有一张表(wordtable),原型是我们前面整理的汉语拼音对照表,正如我们前面所知,这个表有两个字段:汉字本身(在表里用

8、 m_Word 表示)与其对应的拼音(在表里用 m_Sound 表示) 。根据命题三,每一个汉字都与一个唯一的整数对应,因此 6768 个汉字在这张表里是按这些整数的从小到大的顺序来排列的。另外为了算法的实现,我们又加了两个额外的字段,一个是:bool m_Tag ,初始化为0,这是一个标志,因为我们知道,汉字里同音字比较多,当对一个拼音对应的语音数据进行处理之后,这个语音数据在 fsnd.dat 中的偏移量就已确定,那么其它同音的汉字的对应语音数据的偏移量也是同一个值,此时就把这些同音字的这个标志置为1,以后看到这个标志为 1 就跳到下一汉字。另一个额外的字段是:long m_sndInde

9、x, 顾名思义,就是记录该汉字语音数据在此文件中的偏移量,初始为0。又因为 fsnd.dat 是由两部份构成:索引部份与数据部份,为了方便,将这两部份分成两个文件来处理,然后再把包含语音数据的这个文件(totalsnd.dat)的全部内容写到 fsnd.dat 的后面。因为我们前面说过索引部份可看作一个数组,故在程序中我们又设计了一个数组:DWORD SndIdx6768,来充当数据库与 fsnd.dat 的中转。以下是具体的程序部份:m_DbSnd.Open( “sndDB“, false, false, “ODBC; UID = sa “ ); /m_DbSnd 为数据库对象m_pSndS

10、et = new CSndSet( /m_pSndSet 为指向数据集的指针m_pSndSet-Open ();/打开数据集fsnd.Open( “fsnd.dat“, CFile:modeCreate | CFile:modeWrite );/fsnd 为一文件对象DWORD idex = 0 ; /从第一个 wav 文件到第 i 个 wav 文件的长度的累加和DWORD sndLen = 0; /记录第 i 个 wav 文件的长度char pszFilePath50 = “E:myworkhhwwav“; /语音文件位于该目录下char *pFileKind = “.wav“; /语音文件

11、是.wav 文件CFile snd; /代表具体汉字的语音文件对象Cfile totalsnd( “totalsnd.dat“ , CFile:modeCreate | CFile:modeWrite );/全部语音文件数据写入 totalsnd.datCString strsnd; /记录从数据库里读出的汉字的汉语拼音char *sndBuff = NULL; /为第 i 个 wav 文件数据开辟缓冲区int i=0;CString strSQL;/要执行的 SQL 查询、更新语句while( !m_pSndSet-IsEOF() )/从数据库里的第一条记录读到最后一条if( m_pSndS

12、et-m_tag = TRUE ) /说明该字与前面某个字同音SndIdxi = m_pSndSet-m_sndIndex ;fsnd.Write(/将该字发音数据所在位置写入文件i+ ;m_pSndSet-MoveNext ();continue ;strsnd = m_pSndSet-m_sound ;strcat( pszFilePath , strsnd );/具体汉字语音数据文件名与其拼音是一致的strcat( pszFilePath , pFileKind );/给出该字发音文件的完整路径snd.Open( pszFilePath , CFile:modeRead | CFile:

13、typeBinary ); /打开相应的发音文件strcpy( pszFilePath, “E:myworkhhwwav“ ); /初始化下个汉字发音文件的路径/将与该字同音的汉字打上标志,并置发音索引为一样strSQL.Format(“update wordtable set m_tag=1,m_sndIndex=%d where m_sound=%s“, idex, strsnd );m_DbSnd.ExecuteSQL( strSQL );SndIdxi = idex ;fsnd.Write( /将该字发音数据所在位置写入文件i+;sndLen = snd.GetLength(); /得

14、到该字发音文件的大小idex += sndLen ; /紧接的与该汉字不同音的下一汉字的发音在文件中的偏移量if( sndBuff != NULL ) free( sndBuff );sndBuff = NULL;sndBuff = ( char* )malloc( sndLen );/为该 wav 文件数据分配缓冲区snd.ReadHuge( sndBuff, sndLen ) ; / totalsnd.SeekToEnd();totalsnd.WriteHuge( sndBuff, sndLen);/ 把缓冲区语音数据写到语音数据文件snd.Close();/关闭该读音的 wav 文件 m

15、_pSndSet-Move( i ); /转向下一个汉字totalsnd.Close();/关闭语音数据文件/以读的且二进制的方式打开语音数据文件以把它的数据并到 fsnd.dat 里去totalsnd.Open( “totalsnd.dat“, CFile:modeRead | CFile:typeBinary );sndLen = totalsnd.GetLength(); if( sndBuff != NULL ) free( sndBuff );sndBuff = NULL;sndBuff = ( char* )malloc( sndLen );totalsnd.ReadHuge( s

16、ndBuff , sndLen );/把全部语音数据写到缓冲区里totalsnd.Close();fsnd.WriteHuge( sndBuff , sndLen ); /把全部语音数据写到索引部份后面fsnd.Close();/fsnd.dat 制作完毕,关闭文件对象至此这个语音系统的主体部份fsnd.dat 已经完成。考虑到这个语音系统只是我们要开发的客户系统的一个外挂程序,于是把库文件的调用部份做成一个动态链接库,即一个 dll文件。在这个 dll 文件中输出了一个类。这个类是这样定义的:class CTextSnd public:CTextSnd();virtual CTextSnd(

17、); void Load(); /通过新开的一个线程调用 LoadSnd,void UnLoad( void );清空缓冲区数据。BOOL LoadSnd();/打开 fsnd.dat,载入声音数据到 m_szSndBuff,索引数据到 SndIdx6768BOOL Play( char * szText );/szText 是一段需要语音输出的文字,用 Play(char* szText)来完成private:char * m_szSndBuff;DWORD SndIdx6768; 由于语音输出功能是在系统处理业务的时候才调用,故语音输出与这些业务的进行应该是并发的,为此单独开了一个线程来处

18、理语音输出,这是一个全程函数:DWORD WINAPI LoadSndThread(CTextSnd* ptr) ptr-LoadSnd(); return 0; 我们是这样调用这个全程函数的:void CTextSnd:Load()DWORD ThreadId;CloseHandle(CreateThread(NULL,0,(unsigned long(_stdcall*)(void*)LoadSndThread,this,0,最后再给出用于最终输出语音的函数:BOOL CTextSnd:Play(char* szText )。BOOL CTextSnd:Play( char * szTex

19、t )/szText 为一指针,指向要语音输出的那段话if( m_szSndBuff = NULL) return FALSE;DWORD dwSndPosition;char * pT = szText;while( *pT )/(BYTE)pT0为汉字的第一个字节,(BYTE)pT1 为汉字的第二个字节DWORD dwp = ( (BYTE)pT0 - 0xb0 ) * 94 + (BYTE)pT1 - 0xa1 ) ;pT += 2;/转到下一个汉字dwSndPosition = SndIdxdwp ;sndPlaySound( m_szSndBuff + dwSndPosition,

20、SND_SYNC | SND_MEMORY );3return TRUE;调用这个语音系统的时候,例如要输出一段话- m_strWord 的语音(Cstring m_strWord)。我们可以这样做:CtextSnd *TextSnd = new CtextSnd() ;TextSnd-Load();LPTSTR p = m_strWord.GetBuffer( m_strWord.GetLength() ); TextSnd-Play( p );/播放语音至此,我们这个利用汉字编码特性构造函数来实现的语音系统已经全部完成。值得一提的是,这个语音系统在云南某地下属各烟草公司的烟叶收购中得到应用,并收到了良好的效果。参考文献1 Angelika Langer, Klaus Kreft 著,何渝等译标准 C+输入输出流与本地化 北京:人民邮电出版社 20012、 新华字典 北京:商务印书馆 1998 3、 朱友芹等 新编 Windows API 参考大全 电子工业出版社 2000

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 实用文档 > 往来文书

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报