基于汉字编码特性构造函数实现的语音系统_第1页
基于汉字编码特性构造函数实现的语音系统_第2页
基于汉字编码特性构造函数实现的语音系统_第3页
基于汉字编码特性构造函数实现的语音系统_第4页
基于汉字编码特性构造函数实现的语音系统_第5页
已阅读5页,还剩3页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

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

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

3、P = f(Q) = (Q1 0xB0) × 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,如此等等。 

4、;2.关于汉字拼音对照表通过考查新华字典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函数来输出该字的发音:sndPlaySound(

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

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

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

10、t = new CSndSet( &m_DbSnd ) ; /m_pSndSet为指向数据集的指针m_pSndSet->Open ();/打开数据集    fsnd.Open( "fsnd.dat", CFile:modeCreate | CFile:modeWrite );/fsnd为一文件对象         DWORD idex = 0 ; /从第一个wav文件到第i个wav文件的长度的累加和    

11、60;     DWORD sndLen = 0; /记录第i个wav文件的长度         char pszFilePath50 = "E:myworkhhwwav" /语音文件位于该目录下         char *pFileKind = ".wav" /语音文件是.wav文件      

12、            CFile  snd; /代表具体汉字的语音文件对象         Cfile  totalsnd( "totalsnd.dat" , CFile:modeCreate | CFile:modeWrite );/全部语音文件数据写入totalsnd.dat        

13、         CString  strsnd; /记录从数据库里读出的汉字的汉语拼音         char *sndBuff = NULL; /为第i个wav文件数据开辟缓冲区          int i=0;         CString str

14、SQL;/要执行的SQL查询、更新语句         while( !m_pSndSet->IsEOF() )/从数据库里的第一条记录读到最后一条                            if( m_pSndSet->m_tag =

15、 TRUE ) /说明该字与前面某个字同音                                               SndIdxi

16、 = m_pSndSet->m_sndIndex ;                            fsnd.Write(&SndIdxi, sizeof(DWORD);/将该字发音数据所在位置写入文件         

17、;                   i+ ;                            m_pSndSet->MoveNext

18、();                            continue ;                     

19、60;                strsnd = m_pSndSet->m_sound ;                   strcat( pszFilePath , strsnd );/具体汉字语音数据文件名与其拼音是一致的  &#

20、160;                strcat( pszFilePath , pFileKind );/给出该字发音文件的完整路径                          &

21、#160;                                       snd.Open( pszFilePath , CFile:modeRead | CFile:typeBinary ); /打开相应的发音文件

22、                                      strcpy( pszFilePath, "E:myworkhhwwav" );    /初始化下个汉字发音文件的

23、路径                                      /将与该字同音的汉字打上标志,并置发音索引为一样         &#

24、160;         strSQL.Format("update wordtable set m_tag=1,m_sndIndex=%d where m_sound='%s'", idex, strsnd );                   m_DbSnd.ExecuteSQL( strSQL

25、 );                                      SndIdxi = idex ;          

26、60;        fsnd.Write( &SndIdxi, sizeof(DWORD);/将该字发音数据所在位置写入文件                   i+;              &#

27、160;                       sndLen = snd.GetLength(); /得到该字发音文件的大小                   idex += sndLen ; /紧接的

28、与该汉字不同音的下一汉字的发音在文件中的偏移量                   if( sndBuff != NULL )                          &

29、#160;                     free( sndBuff );                          &#

30、160; sndBuff      = NULL;                                      sndBuff = ( char* )malloc( sndLe

31、n );/为该wav文件数据分配缓冲区                   snd.ReadHuge( sndBuff, sndLen ) ; /                        

32、60;              totalsnd.SeekToEnd();                   totalsnd.WriteHuge( sndBuff, sndLen);/ 把缓冲区语音数据写到语音数据文件     

33、60;             snd.Close();/关闭该读音的wav文件                                m_pSndSet->Move

34、( i ); /转向下一个汉字                                     totalsnd.Close();/关闭语音数据文件         /以读

35、的且二进制的方式打开语音数据文件以把它的数据并到fsnd.dat里去         totalsnd.Open( "totalsnd.dat", CFile:modeRead | CFile:typeBinary );         sndLen = totalsnd.GetLength();            

36、;       if( sndBuff != NULL )                             free( sndBuff );           

37、        sndBuff      =       NULL;                  sndBuff = ( char* )malloc( sndLen );       

38、  totalsnd.ReadHuge( sndBuff , sndLen );/把全部语音数据写到缓冲区里         totalsnd.Close();          fsnd.WriteHuge( sndBuff , sndLen ); /把全部语音数据写到索引部份后面         fsnd.Close();/fsnd.

39、dat制作完毕,关闭文件对象    至此这个语音系统的主体部份fsnd.dat已经完成。考虑到这个语音系统只是我们要开发的客户系统的一个外挂程序,于是把库文件的调用部份做成一个动态链接库,即一个dll文件。在这个dll文件中输出了一个类。这个类是这样定义的:class CTextSnd  public:         CTextSnd();         virtual CTextSnd();

40、              void  Load();                /通过新开的一个线程调用LoadSnd,         void  UnLoad( void );清空缓冲区数据。  &#

41、160;      BOOL  LoadSnd();/打开fsnd.dat,载入声音数据到m_szSndBuff,索引数据到SndIdx6768         BOOL  Play( char * szText );/szText是一段需要语音输出的文字,用Play(char* szText)来完成private:         char  * 

42、;     m_szSndBuff;         DWORD SndIdx6768;                  由于语音输出功能是在系统处理业务的时候才调用,故语音输出与这些业务的进行应该是并发的,为此单独开了一个线程来处理语音输出,这是一个全程函数:DWORD  WINAPI  LoadSndT

43、hread(CTextSnd* ptr) ptr->LoadSnd();  return 0; 我们是这样调用这个全程函数的:void  CTextSnd:Load()         DWORD   ThreadId;         CloseHandle(CreateThread(NULL,0,(unsigned long(_stdcall*)(void*)LoadSndThread,

44、this,0,&ThreadId);最后再给出用于最终输出语音的函数:BOOL     CTextSnd:Play(char* szText )。BOOL        CTextSnd:Play( char * szText )/szText为一指针,指向要语音输出的那段话         if( m_szSndBuff =     NULL) return FALSE;         DWORD   dwSndPosition;         char *         pT = szText;      &

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论