/**
* @file mkwhmm.c
*
*
* @brief 音素列から計算用の結合%HMMを生成する
*
*
*
* @brief Generate compound %HMM instance for recognition from phoneme sequence.
*
*
* @author Akinobu LEE
* @date Fri Feb 18 18:31:40 2005
*
* $Revision: 1.5 $
*
*/
/*
* Copyright (c) 1991-2012 Kawahara Lab., Kyoto University
* Copyright (c) 2000-2005 Shikano Lab., Nara Institute of Science and Technology
* Copyright (c) 2005-2012 Julius project team, Nagoya Institute of Technology
* All rights reserved
*/
/* When not a multi-path mode, initial & accept arc will be stripped and
trans prob to accept state will be stored in accept_ac_a. */
#include
#include
/**
* Calculate total number of states in a phoneme sequence.
*
* @param hdseq [in] phoneme sequence as given by pointer list of logical %HMM
* @param hdseqlen [in] length of above
* @param has_sp [in] indicates where short-pause insertion is possible
* @param hmminfo [in] HMM definition
*
* @return the total number of states in the sequence.
*/
static int
totalstatelen(HMM_Logical **hdseq, int hdseqlen, boolean *has_sp, HTK_HMM_INFO *hmminfo)
{
int i, len;
len = 0;
for (i=0;isp) - 2;
}
}
}
if (hmminfo->multipath) {
/* add count for the initial and final state */
len += 2;
}
return(len);
}
/**
* Add a transition arc on the HMM state.
*
* @param state [out] HMM state to add the arc
* @param arc [in] state id of destination
* @param a [in] transition log probability
*/
static void
add_arc(HMM_STATE *state, int arc, LOGPROB a)
{
A_CELL *atmp;
atmp = (A_CELL *)mymalloc(sizeof(A_CELL));
atmp->a = a;
atmp->arc = arc;
atmp->next = state->ac;
state->ac = atmp;
}
/* make word(phrase) HMM from HTK_HMM_INFO */
/* LM prob will be assigned for cross-word arcs */
/* new HMM is malloced and returned */
/**
* Make a HMM instance for recognition from phoneme sequence, with connection
* probabiliry given for each phoneme.
*
* @param hmminfo [in] HTK %HMM definitions data
* @param hdseq [in] phoneme sequence as given by pointer list of logical %HMM
* @param hdseqlen [in] length of above
* @param has_sp [in] indicates where short-pause insertion is possible
* @param lscore [in] list of log probability to be added at the emitting
* transition of each phoneme, or NULL if not needed.
*
* @return newly allocated HMM instance generated from the given data.
*/
HMM *
new_make_word_hmm_with_lm(HTK_HMM_INFO *hmminfo, HMM_Logical **hdseq, int hdseqlen, boolean *has_sp, LOGPROB *lscore)
{
HMM *new;
int i,j,n;
int afrom, ato;
LOGPROB logprob;
HTK_HMM_Trans *tr;
int state_num;
if (has_sp) {
if (hmminfo->sp == NULL) {
jlog("Error: mkwhmm: no short-pause model in hmminfo\n");
return NULL;
}
}
/* allocate needed states */
new = (HMM *)mymalloc(sizeof(HMM));
new->len = totalstatelen(hdseq, hdseqlen, has_sp, hmminfo);
new->state = (HMM_STATE *)mymalloc(sizeof(HMM_STATE) * new->len);
for (i=0;ilen;i++) {
new->state[i].ac = NULL;
new->state[i].is_pseudo_state = FALSE;
new->state[i].out.state = NULL;
new->state[i].out.cdset = NULL;
}
/* assign outprob informations into the states */
n = 0;
if (hmminfo->multipath) n++; /* skip first state */
for (i = 0; i < hdseqlen; i++) {
if (hdseq[i]->is_pseudo) {
for (j = 1; j < hdseq[i]->body.pseudo->state_num - 1; j++) {
new->state[n].is_pseudo_state = TRUE;
new->state[n].out.cdset = &(hdseq[i]->body.pseudo->stateset[j]);
n++;
}
} else {
for (j = 1; j < hdseq[i]->body.defined->state_num - 1; j++) {
new->state[n].is_pseudo_state = FALSE;
new->state[n].out.state = hdseq[i]->body.defined->s[j];
n++;
}
}
if (has_sp) {
if (has_sp[i]) {
/* append sp at the end of the phone */
if (hmminfo->sp->is_pseudo) {
for (j = 1; j < hmm_logical_state_num(hmminfo->sp) - 1; j++) {
new->state[n].is_pseudo_state = TRUE;
new->state[n].out.cdset = &(hmminfo->sp->body.pseudo->stateset[j]);
n++;
}
} else {
for (j = 1; j < hmm_logical_state_num(hmminfo->sp) - 1; j++) {
new->state[n].is_pseudo_state = FALSE;
new->state[n].out.state = hmminfo->sp->body.defined->s[j];
n++;
}
}
}
}
}
/* make transition arcs between each state*/
/*
* for (i=0;idef->state_num;i++) {
* if (i != 1 && (hdseq[0]->def->tr->a[0][i]) != LOG_ZERO) {
* jlog("initial state contains more than 1 arc.\n");
* }
* }
*/
if (hmminfo->multipath) {
int *out_from, *out_from_next;
LOGPROB *out_a, *out_a_next;
int out_num_prev, out_num_next;
out_from = (int *)mymalloc(sizeof(int) * new->len);
out_from_next = (int *)mymalloc(sizeof(int) * new->len);
out_a = (LOGPROB *)mymalloc(sizeof(LOGPROB) * new->len);
out_a_next = (LOGPROB *)mymalloc(sizeof(LOGPROB) * new->len);
n = 0; /* n points to previous state */
out_from[0] = 0;
out_a[0] = 0.0;
out_num_prev = 1;
for (i = 0; i < hdseqlen; i++) {
state_num = hmm_logical_state_num(hdseq[i]);
tr = hmm_logical_trans(hdseq[i]);
out_num_next = 0;
/* arc from initial state */
for (ato = 1; ato < state_num; ato++) {
logprob = tr->a[0][ato];
if (logprob != LOG_ZERO) {
/* expand arc */
if (ato == state_num-1) {
/* from initial to final ... register all previously registered arcs for next expansion */
if (lscore != NULL) logprob += lscore[i];
for(j=0;jstate[out_from[j]]), n + ato,
out_a[j] + logprob);
}
}
}
}
/* arc from output state */
for(afrom = 1; afrom < state_num - 1; afrom++) {
for (ato = 1; ato < state_num; ato++) {
logprob = tr->a[afrom][ato];
if (logprob != LOG_ZERO) {
if (ato == state_num - 1) {
/* from output state to final ... register the arc for next expansion */
if (lscore != NULL) logprob += lscore[i];
out_from_next[out_num_next] = n+afrom;
out_a_next[out_num_next++] = logprob;
} else {
add_arc(&(new->state[n+afrom]), n + ato, logprob);
}
}
}
}
n += state_num - 2;
for(j=0;jsp); ato++) {
logprob = hmm_logical_trans(hmminfo->sp)->a[0][ato];
if (logprob != LOG_ZERO) {
/* to control short pause insertion, transition probability toward
the word-end short pause will be given a penalty */
logprob += hmminfo->iwsp_penalty;
/* expand arc */
if (ato == hmm_logical_state_num(hmminfo->sp)-1) {
/* from initial to final ... register all previously registered arcs for next expansion */
for(j=0;jstate[out_from[j]]), n + ato,
out_a[j] + logprob);
}
}
}
}
/* if short pause model doesn't have a model skip transition, also add it */
if (hmm_logical_trans(hmminfo->sp)->a[0][hmm_logical_state_num(hmminfo->sp)-1] == LOG_ZERO) {
/* to make insertion sp model to have no effect on the original path,
the skip transition probability should be 0.0 (=100%) */
logprob = 0.0;
for(j=0; jsp) - 1; afrom++) {
for (ato = 1; ato < hmm_logical_state_num(hmminfo->sp); ato++) {
logprob = hmm_logical_trans(hmminfo->sp)->a[afrom][ato];
if (logprob != LOG_ZERO) {
if (ato == hmm_logical_state_num(hmminfo->sp) - 1) {
/* from output state to final ... register the arc for next expansion */
out_from_next[out_num_next] = n+afrom;
out_a_next[out_num_next++] = logprob;
} else {
add_arc(&(new->state[n+afrom]), n + ato, logprob);
}
}
}
}
n += hmm_logical_state_num(hmminfo->sp) - 2;
for(j=0;jstate[out_from[j]]), new->len-1, out_a[j]);
}
free(out_from);
free(out_from_next);
free(out_a);
free(out_a_next);
} else {
/* non-multipath version */
new->accept_ac_a = LOG_ZERO;
n = 0;
for (i = 0; i < hdseqlen; i++) {
state_num = hmm_logical_state_num(hdseq[i]);
tr = hmm_logical_trans(hdseq[i]);
/* for each phoneme, consult the transition matrix to form HMM instance */
for (afrom = 1; afrom < state_num - 1; afrom++) {
for (ato = 1; ato < state_num; ato++) {
logprob = tr->a[afrom][ato];
if (logprob != LOG_ZERO) {
/* if emitting transition, add connection probability to the arc */
if (ato == state_num - 1 && lscore != NULL){
logprob += lscore[i];
}
if (n + (ato - afrom) >= new->len) { /* arc to accept node */
if (new->accept_ac_a != LOG_ZERO) {
jlog("Error: mkwhmm: more than 1 arc to accept node found\n");
return NULL;
} else {
new->accept_ac_a = logprob;
}
} else {
add_arc(&(new->state[n]), n + (ato - afrom), logprob);
}
}
}
n++;
}
}
}
return (new);
}
/**
* Make a HMM instance for recognition from phoneme sequence.
*
* @param hmminfo [in] HTK %HMM definitions data
* @param hdseq [in] phoneme sequence as given by pointer list of logical %HMM
* @param hdseqlen [in] length of above
* @param has_sp [in] indicates where short-pause insertion is possible
*
* @return newly allocated HMM instance generated from the given data.
*/
HMM *
new_make_word_hmm(HTK_HMM_INFO *hmminfo, HMM_Logical **hdseq, int hdseqlen, boolean *has_sp)
{
return(new_make_word_hmm_with_lm(hmminfo, hdseq, hdseqlen, has_sp, NULL));
}
/**
* Free an HMM instance.
*
* @param d [in] HMM instance to free
*/
void
free_hmm(HMM *d)
{
A_CELL *ac, *atmp;
int i;
for (i=0;ilen;i++) {
ac = d->state[i].ac;
while (ac) {
atmp = ac->next;
free(ac);
ac = atmp;
}
}
free(d->state);
free(d);
}