HCompV源码再解析
作者:互联网
在还没开始讲解源码之前,我先贴出一个数据结构HMMSet:
/* ---------------------- HMM Sets ----------------------------- */
typedef struct _HMMSet{
MemHeap *hmem; /* memory heap for this HMM Set */
Boolean *firstElem; /* first element added to hmem during MakeHMMSet*/
char *hmmSetId; /* identifier for the hmm set */
MILink mmfNames; /* List of external file names */
int numLogHMM; /* Num of logical HMM's */
int numPhyHMM; /* Num of distinct physical HMM's */
int numFiles; /* total number of ext files */
int numMacros; /* num macros used in this set */
MLink * mtab; /* Array[0..MACHASHSIZE-1]OF MLink */
PtrMap ** pmap; /* Array[0..PTRHASHSIZE-1]OF PtrMap* */
Boolean allowTMods; /* true if HMMs can have Tee Models */
Boolean optSet; /* true if global options have been set */
short vecSize; /* dimension of observation vectors */
short swidth[SMAX]; /* [0]=num streams,[i]=width of stream i */
ParmKind pkind; /* kind of obs vector components */
DurKind dkind; /* kind of duration model (model or state) */
CovKind ckind; /* cov kind - only global in V1.X */
HSetKind hsKind; /* kind of HMM set */
TMixRec tmRecs[SMAX]; /* array[1..S]of tied mixture record */
int numStates; /* Number of states in HMMSet */
int numSharedStates; /* Number of shared states in HMMSet */
int numMix; /* Number of mixture components in HMMSet */
int numSharedMix; /* Number of shared mixtures in HMMSet */
int numTransP; /* Number of distinct transition matrices */
int ckUsage[NUMCKIND]; /* Number of components using given ckind */
InputXForm *xf; /* Input transform of HMMSet */
AdaptXForm *semiTied; /* SemiTied transform associated with model set */
short projSize; /* dimension of vector to update */
/* Adaptation information accumulates */
Boolean attRegAccs; /* have the set of accumulates been attached */
Boolean attXFormInfo; /* have the set of adapt info been attached */
Boolean attMInfo; /* have the set of adapt info been attached */
AdaptXForm *curXForm;
AdaptXForm *parentXForm;
/* Added to support LogWgts */
Boolean logWt; /* Component weights are stored as Logs */
/* Added to support delayed loading of the semi-tied transform */
char *semiTiedMacro; /* macroname of semi-tied transform */
} HMMSet;
要想掌握HTK工具,必须对这个结构里的每一项了如指掌,并能清晰的勾勒出画面。随着程序调试的进行,我们逐渐接近这个结构,并把它弄清楚。
在HCompV工具中,调试时直接跳到函数InitShell处。它调用SaveCommandLine函数,它的功能在静态变量char* saveCommandLine中保持命令行参数,后续解析会用到。
static char *savedCommandLine;
/* SaveCommandLine: Stores all command line arguments in 1 string */
static void SaveCommandLine(int argc, char **argv)
{
int i, len=0;
for (i=0;i<argc;i++)
len+=strlen(argv[i])+1;
savedCommandLine = (char *) malloc(len);
savedCommandLine[0]='\000';
strcat(savedCommandLine, argv[0]);
for (i=1;i<argc;i++)
sprintf(savedCommandLine,"%s %s", savedCommandLine, argv[i]);
}
现在它的内容是“D:\\vscode\\source\\repos\\HTK\\HCompV\\Debug\\HCompV.exe -C .\\src\\config\\config1 -f 0.01 -m -S .\\src\\train.scp -M .\\src\\hmms\\hmm0 .\\src\\proto” 一共11个参数,即argc值为11.
接下来的循环处理-A -C -S -V -D标志,并保存相应的参数:
-A表示打印信息,infoPrint设为True
-C是指定配置文件,它的位置有-C后面的命令参数指定;然后是读取配置文件并解析;
函数 ReadConfigFile读取配置文件,并解析,构造confList,它是一个链表,节点是ConfigEntry。
typedef struct _ConfigEntry{
ConfParam param;
struct _ConfigEntry *next;
}ConfigEntry;
typedef struct { /* Configuration Parameter */
char *user; /* name of module/tool to use this param */
char *name; /* name of param - upper case always */
ConfKind kind; /* kind of config param value */
ConfVal val; /* value */
Boolean seen; /* set true when read by any module */
} ConfParam;
param内容包含了配置项的名字、值类型和值本身。
-S指定文件列表文件,后面是文件的具体位置。通过 SetScriptFile函数计算包含多少个文件。
然后就是系统的初始化,其中跟后面密切相关的是内存的初始化。它会分配gstack这个全局的内存空间。在后面的CreateHMMSet函数中,将会看到,HMM集合对象hset有个属性mem,它指向gstack的内存空间,而它上面保存了所有的HMM参数。它们是以哈希表的数据结构存储的。
CreateHMMSet函数,第一个参数hset,就是HMMSet的指针,它指向一个对象包含了当前HMM集合的一些信息,比如:包含多少个hmm(物理的、逻辑的各多少)、而且hmm还分不同类型(这点还没弄明白),以及存储的内存位置。HTK还支持多数据流通道,每个数据流的维度等等信息。
这个结构是像是一个理解HTK的地图,时刻要关注它。关注每一步操作对这个结构产生哪些影响。或者这个结构连接上的其他信息发生哪些变化。
另外,非常重要的一点是,它还包含一个hashtable;而且这个hashtable的位置就在CreateHMMSet函数的第二个参数MemHeap* heap指向的内存块上。
这个hashtable的总槽数由下面这个宏确定:
#define MACHASHSIZE 250007 /* Size of each HMM Set macro hash table */
待会儿会看到如何创建这个hashtable,以及如何给这个table添加第一个hmm对象。下面的代码是CreateHMMSet函数,前面都是初值设定,没有什么难的。重点是看到中间那部分,调用一个特征重要的函数MakeHashTab(hset, MACHASHSIZE)。
/* EXPORT->CreateHMMSet: create the basic HMMSet structure */
void CreateHMMSet(HMMSet *hset, MemHeap *heap, Boolean allowTMods)
{
int s;
/* set default values in hset structure */
hset->hmem = heap;
hset->hmmSetId = NULL;
hset->mmfNames = NULL; hset->numFiles = 0;
hset->allowTMods = allowTMods; hset->optSet = FALSE;
hset->vecSize = 0; hset->swidth[0] = 0;
hset->dkind = NULLD; hset->ckind = NULLC; hset->pkind = 0;
hset->numPhyHMM = hset->numLogHMM = hset->numMacros = 0;
hset->xf = NULL; hset->logWt = FALSE;
for (s=1; s<SMAX; s++) {
hset->tmRecs[s].nMix = 0; hset->tmRecs[s].mixId = NULL;
hset->tmRecs[s].probs = NULL; hset->tmRecs[s].mixes = NULL;
}
/* initialise the hash tables */
hset->mtab = (MLink *)MakeHashTab(hset,MACHASHSIZE);
hset->pmap = NULL;
/* initialise adaptation information */
hset->attRegAccs = FALSE;
hset->attXFormInfo = FALSE;
hset->attMInfo = FALSE;
hset->curXForm = NULL;
hset->parentXForm = NULL;
hset->semiTiedMacro = NULL;
hset->semiTied = NULL;
hset->projSize = 0;
}
下面可以看出函数MakeHashTab(hset, MACHASHSIZE)在创建一个哈希表。
/* MakeHashTab: make a macro hash table and initialise it */
void ** MakeHashTab(HMMSet *hset, int size)
{
void **p;
int i;
p = (void **) New(hset->hmem,sizeof(void *)*size);
for (i=0; i<size; i++)
p[i] = NULL;
return p;
}
这个hash表的每个元素都可能填上一个MLink,它是指向MacroDef的指针。
typedef struct _MacroDef *MLink;
typedef struct _MacroDef{
MLink next; /* next cell in hash table */
char type; /* type of macro [hluvixdtmps*] */
short fidx; /* idx of MMF file (0 = SMF) */
LabId id; /* name of macro */
Ptr structure; /* -> shared structure or HMM Def */
} MacroDef;
这个MacroDef是描述了HMM更上层的结构,它的Ptr structure会指向具体的hmm对象。
到此,我们头脑中,应该有张清晰的地图,就是HTK这个隐马尔可夫(HMM)开源软件最重要的结构图。
标签:NULL,set,int,HMMSet,HCompV,HMM,源码,解析,hset 来源: https://blog.csdn.net/hjx5200/article/details/116305270