首先说下这个函数的主要功能:

第一遍调用时,heap!=null,会创建NetNode节点和links计数。

怎么创建NetNode节点呢?它依据什么呢?就是Lattice中的元素。

我们知道,Lattice叫词格网络,它的节点是词,边表示词之间的连接关系。

所以,一是为每个节点创建一个NetNode;对象存放在wnHashTab中,是n_word类型的。wnHashTab是hash表,它的指针指向NetNode对象。

/* Process the cross word links, the first time heap!=NULL and */
/*  the links are counted and wordnodes created, the second    */
/*  time heap==NULL and link likelihoods/destinations are set. */
void ProcessCrossWordLinks(MemHeap *heap,Lattice *lat,int xc)
{PronHolder *lInst,*rInst;NetNode *wordNode;LArc *thisLArc;int i,lc,rc,type;/*  Currently a new word end is created for all logical contexts *//*  This is only needed for single phone words for which several *//*  models (in different contexts) connect to a single word end. *//*  For multi-phone words no models are shared and so a single   *//*  word end per distinct physical model would be fine.          */for (i=0; i<lat->na; i++) {thisLArc = NumbLArc(lat, i);for (lInst=(PronHolder*)thisLArc->start->sublat;lInst!=NULL;lInst=lInst->next)for (rInst=(PronHolder*)thisLArc->end->sublat;rInst!=NULL;rInst=rInst->next) {if (xc==0) {wordNode = FindWordNode(heap,lInst->pron,lInst,n_word);if (heap!=NULL)wordNode->tag=SafeCopyString(heap,thisLArc->start->tag); if (heap==NULL) {wordNode->links[wordNode->nlinks].node=rInst->starts;wordNode->links[wordNode->nlinks].like=thisLArc->lmlike;}wordNode->nlinks++;}            }
}

上面的代码显示,遍历Lattice网格的所有边,为它的每个边的左节点创建NetNode对象,NetNode的类型是n_word,依据的就是该节点的发音实例信息sublat。Lattice词格中所有节点的发音实例,在上一篇博客提到的InitPronHolders函数中完成了创建。

FindWordNode完成该创建过程。

/* Use hash table to lookup word end node */
static NetNode *FindWordNode(MemHeap *heap,Pron pron,PronHolder *pInst,NetNodeType type)
{union {Ptr ptrs[3];unsigned char chars[12];}un;unsigned int hash,i;NetNode *node;hash=0;un.ptrs[0]=pron;un.ptrs[1]=pInst;un.ptrs[2]=(Ptr)type;for (i=0;i<12;i++)hash=((hash<<8)+un.chars[i])%WNHASHSIZE;for (node=wnHashTab[hash];node!=NULL;node=node->chain)if (node->info.pron==pron && node->inst==(NetInst*)pInst &&node->type==type) break;if (node==NULL) {nwe++;node=(NetNode *) New(heap,sizeof(NetNode));node->info.pron=pron;node->type=type;node->inst=(NetInst*)pInst;node->nlinks=0;node->links=NULL;node->tag=NULL;node->aux=0;node->chain=wnHashTab[hash];wnHashTab[hash]=node;}return(node);
}

就是在wnHashTab中找个位置,然后new一个NetNode对象,指向它,并给它赋值,包括pron、type、并把chain指向自己。

其中wnHashTab是个hash表,存放了词节点的信息,上面的这个节点为典型。

第二遍调用ProcessCrossWordLinks时heap==null,这时走下面的分支:

               if (heap==NULL) {wordNode->links[wordNode->nlinks].node=rInst->starts;wordNode->links[wordNode->nlinks].like=thisLArc->lmlike;}

这里有两个问题,一是links的空间是否创建了;二是rInst->starts指向的什么?

在两次调用ProcessCrossWordLinks之间的代码,就是消除上面两个问题的。

 /* Build models on basis of contexts seen */net->teeWords=FALSE;for (i=0; i < lat->nn; i++) {thisLNode = lat->lnodes+i;thisWord = thisLNode->word;for(pInst=(PronHolder*)thisLNode->sublat;pInst!=NULL;pInst=pInst->next) {p=0;q=pInst->nphones-1;pInst->tee=TRUE;/* Make wrd-int cd phones (possibly none!) */CreateWIModels(pInst,p,q,net,hci);if (hci->xc==0) {/* Word internal context only */CreateIEModels(thisWord,pInst,p,q,net,hci);} }}

这段代码是处理词内部的节点指向关系的。处理特殊情况的代码我删了,比如!NULL节点,这时它的nphones==0。

我们在发音字典的每个词发音音子序列后面都添加了sp音子。

看下CreateWIModels函数的细节:

void CreateWIModels(PronHolder *pInst,int p,int q, Network *net,HMMSetCxtInfo *hci)
{NetNode *node;HLink hmm;int j;for(j=q-1;j>p;j--) {hmm=GetHCIModel(hci,FindLContext(hci,pInst,j,0),pInst->phones[j],FindRContext(hci,pInst,j,0));if (hmm->transP[1][hmm->numStates]<LSMALL) pInst->tee=FALSE;nwi++;node=NewNode(net->heap,hmm,(pInst->chain==NULL?0:1));if (pInst->chain!=NULL) {nil++;node->links[0].node=pInst->chain;node->links[0].like=pInst->fct;}node->chain=pInst->chain;pInst->chain=node;}
}

这时for循环里可以把j~p看作指针指向音子。刚开始时,j指向的是v,p指向s;hmm分别为v、iy、t,通过NewNode构建节点。然后添加到pInst的chain后面。

其他两个音子没做处理,留给下一个函数来处理,因为它们涉及到跨词扩展的设置。因为我们当前是单音子模型,所以不存在这个问题。

void CreateIEModels(Word thisWord,PronHolder *pInst,int p,int q,Network *net,HMMSetCxtInfo *hci)
{NetNode *node,*wordNode;HLink hmm;/* End */hmm=GetHCIModel(hci,FindLContext(hci,pInst,q,0),pInst->phones[q],0);if (hmm->transP[1][hmm->numStates]<LSMALL) pInst->tee=FALSE;wordNode = FindWordNode(NULL,pInst->pron,pInst,n_word);nfi++; nil++;node=NewNode(net->heap,hmm,1);node->links[0].node=wordNode;node->links[0].like=pInst->fct;pInst->ends=node;pInst->nend=1;/* Start */hmm=GetHCIModel(hci,0,pInst->phones[p],FindRContext(hci,pInst,p,0));if (hmm->transP[1][hmm->numStates]<LSMALL) pInst->tee=FALSE;nin++; nil++;node=NewNode(net->heap,hmm,1);node->links[0].node=(pInst->chain?pInst->chain:pInst->ends);node->links[0].like=pInst->fct;pInst->starts=node;pInst->nstart=1;/* Chain */if (pInst->chain!=NULL) {for (node=pInst->chain;node->chain!=NULL;node=node->chain);node->nlinks=1;nil++;node->links=(NetLink*) New(net->heap,sizeof(NetLink));node->links[0].node=pInst->ends;node->links[0].like=pInst->fct;}}

在注释/* End */下面部分的代码,就是处理最后一个音子sp的。

先是获取sp的hmm模型,然后通过pInst构建/查找到词节点也就是STEVE,就是wordNode。然后,New一个sp对应的hmm节点,且设置它的连接数为1。它指向前面的wordNode。我们知道,每个发音音子序列的最后一个音子将指向词节点。如下图所示,其更新部分就是/* End */代码完成的。

接着看/* Start */部分。

创建s节点,并将stats指向该节点,且pInst->chain接在该节点的links后面。

这里可以看出,如果要按次序进行token传递,顺序应该是这样的:starts --> chains ....  --> ends。

这时,我应该就能理解了,为什么要分两次调用ProcessCrossWordLinks。第一遍时完成词的创建,然后建立词内的模型之间的串联关系,最后再调用一遍ProcessCrossWordLinks,完成词到词的跨越,而rInst->starts指向LArc右边节点的发音实例(PronHolder)的初始音子模型。

HTK中函数ProcessCrossWordLinks处理流程相关推荐

  1. android 模拟器识别,一种基于符号的识别Android应用运行在模拟器中的方法与流程...

    本发明涉及一种基于符号的识别android应用运行在模拟器中的方法. 背景技术: 很多的移动应用都会使用一种基于lbs的地理围栏的技术(如移动打卡),某些用户为了突破这种围栏的限制,将移动应用运行在模 ...

  2. c# 定位内存快速增长_CTF丨Linux Pwn入门教程:针对函数重定位流程的相关测试(下)...

    Linux Pwn入门教程系列分享已到尾声,本套课程是作者依据i春秋Pwn入门课程中的技术分类,并结合近几年赛事中出现的题目和文章整理出一份相对完整的Linux Pwn教程. 教程仅针对i386/am ...

  3. Python 函数的执行流程-函数递归-匿名函数-生成器

    1 函数的执行流程 函数的执行需要对函数进行压栈的,什么是压栈呢,简而言之就是在函数执行时在栈中创建栈帧存放需要变量以及指针的意思.具体涉及的知识非常多,这里就已一个Python脚本简单进行分析. 当 ...

  4. 内核中的UDP socket流程(1)

    内核中的UDP socket流程(1)  相对于TCP,UDP协议要简单的多.所以我决定由简入繁,先从UDP协议入手. 前一遍文章已经确定了struct sk_buff被用于socket的接受和发送缓 ...

  5. 梆梆加固函数抽取执行流程

    梆梆加固函数抽取执行流程:函数的第一条指令是goto,然后中间是一系列的nop(预留空间),第一条指令goto到末尾,跳过预留空间,跳转到的位置是一条invoke指令,调用壳中的还原函数,还原函数会将 ...

  6. GPU随想——OpenGL函数加载流程

    导航: GLX基本流程 OpenGL函数的分发 到底什么是context? -----------------------------读前须知------------------------- 历代d ...

  7. TDA4VM 8.4 RTOS SDK 中 SBL 的编译流程解析与makefile 分析笔记

    目录 0. TDA4VM 8.4 RTOS SDK 中 SBL 的编译流程解析 TI SDK 的可执行文件后缀名是 .xer5f 的原因 sbl_ospi_img 的 .xer5f 可执行文件的生成过 ...

  8. 对ajax中onreadystatechange事件执行流程的理解

    对Ajax中onreadystatechange事件执行流程的理解 onreadystatechange 事件 当请求被发送到服务器时,我们需要执行一些基于响应的任务. 每当 readyState 改 ...

  9. JavaScript中函数四种调用模式

    目录 JS中函数的四种调用模式 函数调用模式 方法调用模式 构造器调用模式 上下文调用模式 JS中函数的四种调用模式 在函数的调用模式中感觉最大的区别就是: this指向 函数调用模式 即通过函数名直 ...

  10. python参数传递方法_深入理解python中函数传递参数是值传递还是引用传递

    python 的 深入理解python中函数传递参数是值传递还是引用传递 目前网络上大部分博客的结论都是这样的: Python不允许程序员选择采用传值还是传 引用.Python参数传递采用的肯定是&q ...

最新文章

  1. 从面试官角度观察到的程序员工资瓶颈,同时给出突破瓶颈的建议
  2. 解决chrome安装扩展插件导入.crx文件提示:程序包无效的错误
  3. python实现编译器连接器_Python连接器
  4. 使用EF Core操作层次结构数据
  5. mysql语句怎么记_mysql语句记录
  6. 【AI视野·今日CV 计算机视觉论文速览 第212期】Thu, 3 Jun 2021
  7. 梦见有人追我我拼命逃
  8. Atitit.ide eclipse编译速度慢的解决
  9. 从零开始系统学习区块链
  10. java 定义泛型方法_Java泛型类与泛型方法的定义详解
  11. POJ 1265 Area (皮克公式+多边形面积)
  12. 风险评估-HEAVENS
  13. CSP-S2020总结
  14. linux下部署项目,下载文件时,中文文件名乱码问题
  15. html+ul隐藏滚动条,2种方法实现CSS隐藏滚动条并可以滚动内容的方法
  16. 【项目】数仓项目(四)
  17. 新电脑安装python环境
  18. 必读论文|百篇最值得一读的“认知图谱”经典论
  19. 颁奖盛典完美落幕 硬创大咖群英汇聚
  20. EDXposed| 小米8

热门文章

  1. ZooKeeper 会话的秘密 快偷偷过来瞧一瞧!!!
  2. 抖音直播下载方法(附视频下载方法)
  3. 指标公式c语言源码下载,通达信最全指标公式源码,给大家分享实用的技术指标...
  4. java被安全阻止解决及用友Uclient安装
  5. QTP 9.2 下载破解
  6. java 身份证地址提取籍贯_函数 | 身份证提取籍贯信息
  7. STM32命名规则 STM32选型手册
  8. 信号与系统——初识信号的原理与分类
  9. java heapdump 分析工具_Heapdump分析软件
  10. jmeter录制脚本(代理)