/** 
 * <JA>
 * @brief  木構造化辞書上の 1-gram factoring 値を計算して格納する. 
 *
 * 1-gram factoring では単語間で共有されている枝ノードでは 1-gram の最大値
 * を与える. 単語履歴によらないため,その値は認識開始前に
 * 計算しておくことができる. この関数は木構造化辞書
 * 全体について,共有されている(successor list に2つ以上の単語を持つノード)
 * ノードの 1-gram factoring 値を計算して格納する. 1-gram factoring値を
 * 計算後は,そのノードの successor list はもはや不要であるため,ここで
 * 削除する. 
 *
 * 実際には,factoring 値は wchmm->fscore に順次保存され,ノードの
 * scid にその保存値へのインデックス(1-)の負の値が格納される. 不要になった
 * successor list は,実際には compaction_successor 内で,対応するノードの
 * scid が負になっている successor list を削除することで行なわれる. 
 * 
 * @param wchmm [i/o] 木構造化辞書
 * </JA>
 * <EN>
 * @brief  Calculate all the 1-gram factoring values on tree lexicon.
 *
 * On 1-gram factoring, the shared nodes on branch has fixed factoring score
 * from 1-gram values, independent of the word context on recognition.  So
 * the values are fixed for all recognition and can be calculated before
 * search.  This function stores all the neede 1-gram factoring value by
 * traversing tree lexicon with successor lists and compute maximum 1-gram
 * for each successor lists that has more than two words (=shared).
 * Since a successor list is no more neede after the 1-gram value is computed,
 * they will be freed.
 *
 * Actually, computed factoring scores will be stored in wchmm->fscore
 * sequencially, and the index value, starting from 1,
 * to the fscore list is stored in scid of each nodes as a negative value.
 * The free will be performed in compaction_successor() by checking if a
 * successor's corresponding scid on tree lexicon has negative value.
 * 
 * @param wchmm [i/o] tree lexicon
 * </EN>
 *
 * @callgraph
 * @callergraph
 * 
 */
void
calc_all_unigram_factoring_values(WCHMM_INFO *wchmm)
{
  S_CELL *sc, *sctmp;
  LOGPROB tmpprob, maxprob;
  int i, n;

  /* count needed number of 1-gram factoring nodes */
  n = 0;
  for (i=1;i<wchmm->scnum;i++) {
    sc = wchmm->sclist[i];
    if (sc == NULL) {
      j_internal_error("call_all_unigram_factoring_values: sclist has no sc?\n");
    }
    if (sc->next != NULL) {
      /* more than two words, so compute maximum 1-gram probability */
      n++;
    }
  }
  wchmm->fsnum = n + 1;
  /* allocate area */
  wchmm->fscore = (LOGPROB *)mymalloc(sizeof(LOGPROB) * wchmm->fsnum);
  /* assign values */
  n = 1;
  for (i=1;i<wchmm->scnum;i++) {
    sc = wchmm->sclist[i];
    if (sc->next != NULL) {
      maxprob = LOG_ZERO;
      for (sctmp = sc; sctmp; sctmp = sctmp->next) {
	if (wchmm->ngram) {
	  tmpprob = uni_prob(wchmm->ngram, wchmm->winfo->wton[sctmp->word])
#ifdef CLASS_NGRAM
	    + wchmm->winfo->cprob[sctmp->word] 
#endif
	    ;
	} else {
	  tmpprob = LOG_ZERO;
	}
	if (wchmm->lmvar == LM_NGRAM_USER) {
	  tmpprob = (*(wchmm->uni_prob_user))(wchmm->winfo, sctmp->word, tmpprob);
	}
	if (maxprob < tmpprob) maxprob = tmpprob;
      }
      wchmm->fscore[n] = maxprob;
      free_successor(wchmm, i);
      wchmm->state[wchmm->sclist2node[i]].scid = - n;
      n++;
    }
  }
  /* garbage collection of factored sclist */
  compaction_successor(wchmm);
}
Beispiel #2
0
/** 
 * <EN>
 * @brief  Split input parameter for segmentation.
 * 
 * Copy the rest samples in param to rest_param, and shrink the param
 * in mfcc instance.  [start...param->samplenum] will be copied to
 * rest_param, and [0...end] will be left in param.
 * </EN>
 * <JA>
 * @brief  セグメンテーション時に入力パラメータを分割する. 
 * 
 * 残りのサンプル(現在のフレームから終わりまで)を rest_param に
 * コピーし,元の param を短くする. [start...param->samplenum] が
 * rest_param にコピーされ,元の param には [0...end] が残る. 
 * </JA>
 * 
 * @param mfcc [i/o] MFCC calculation instance
 * @param start [in] copy start frame
 * @param end [in] original end frame
 * 
 * @callgraph
 * @callergraph
 */
void
mfcc_copy_to_rest_and_shrink(MFCCCalc *mfcc, int start, int end)
{
  int t;

  /* copy rest parameters for next process */
  mfcc->rest_param = new_param();
  memcpy(&(mfcc->rest_param->header), &(mfcc->param->header), sizeof(HTK_Param_Header));
  mfcc->rest_param->samplenum = mfcc->param->samplenum - start;
  mfcc->rest_param->header.samplenum = mfcc->rest_param->samplenum;
  mfcc->rest_param->veclen = mfcc->param->veclen;
  if (param_alloc(mfcc->rest_param, mfcc->rest_param->samplenum, mfcc->rest_param->veclen) == FALSE) {
    j_internal_error("ERROR: segmented: failed to allocate memory for rest param\n");
  }
  /* copy data */
  for(t=start;t<mfcc->param->samplenum;t++) {
    memcpy(mfcc->rest_param->parvec[t-start], mfcc->param->parvec[t], sizeof(VECT) * mfcc->rest_param->veclen);
  }
  
  /* shrink original param */
  /* just shrink the length */
  mfcc->param->samplenum = end;
}
/** 
 * <JA>
 * @brief  第1パス平行認識処理の準備
 *
 * 計算用変数をリセットし,各種データを準備する. 
 * この関数は,ある入力(あるいはセグメント)の認識が
 * 始まる前に呼ばれる. 
 * 
 * </JA>
 * <EN>
 * @brief  Preparation for the on-the-fly 1st pass decoding.
 *
 * Variables are reset and data are prepared for the next input recognition.
 *
 * This function will be called before starting each input (segment).
 * 
 * </EN>
 *
 * @param recog [i/o] engine instance
 *
 * @return TRUE on success. FALSE on failure.
 *
 * @callgraph
 * @callergraph
 * 
 */
boolean
RealTimePipeLinePrepare(Recog *recog)
{
  RealBeam *r;
  PROCESS_AM *am;
  MFCCCalc *mfcc;
#ifdef SPSEGMENT_NAIST
  RecogProcess *p;
#endif

  r = &(recog->real);

  /* 計算用の変数を初期化 */
  /* initialize variables for computation */
  r->windownum = 0;
  /* parameter check */
  for(mfcc = recog->mfcclist; mfcc; mfcc = mfcc->next) {
    /* パラメータ初期化 */
    /* parameter initialization */
    if (recog->jconf->input.speech_input == SP_MFCMODULE) {
      if (mfc_module_set_header(mfcc, recog) == FALSE) return FALSE;
    } else {
      init_param(mfcc);
    }
    /* フレームごとのパラメータベクトル保存の領域を確保 */
    /* あとで必要に応じて伸長される */
    if (param_alloc(mfcc->param, 1, mfcc->param->veclen) == FALSE) {
      j_internal_error("ERROR: segmented: failed to allocate memory for rest param\n");
    }
    /* フレーム数をリセット */
    /* reset frame count */
    mfcc->f = 0;
  }
  /* 準備した param 構造体のデータのパラメータ型を音響モデルとチェックする */
  /* check type coherence between param and hmminfo here */
  if (recog->jconf->input.paramtype_check_flag) {
    for(am=recog->amlist;am;am=am->next) {
      if (!check_param_coherence(am->hmminfo, am->mfcc->param)) {
	jlog("ERROR: input parameter type does not match AM\n");
	return FALSE;
      }
    }
  }

  /* 計算用のワークエリアを準備 */
  /* prepare work area for calculation */
  if (recog->jconf->input.type == INPUT_WAVEFORM) {
    reset_mfcc(recog);
  }
  /* 音響尤度計算用キャッシュを準備 */
  /* prepare cache area for acoustic computation of HMM states and mixtures */
  for(am=recog->amlist;am;am=am->next) {
    outprob_prepare(&(am->hmmwrk), r->maxframelen);
  }

#ifdef BACKEND_VAD
  if (recog->jconf->decodeopt.segment) {
    /* initialize segmentation parameters */
    spsegment_init(recog);
  }
#else
  recog->triggered = FALSE;
#endif

#ifdef DEBUG_VTLN_ALPHA_TEST
  /* store speech */
  recog->speechlen = 0;
#endif

  return TRUE;
}
/** 
 * <JA>
 * 構築された factoring 情報を multipath 用に調整する. factoring 情報を,
 * モデル全体をスキップする遷移がある場合はその先の音素へコピーする. 
 * また,(出力を持たない)文頭文法ノードに単語先頭ノードからコピーする. 
 * 
 * @param wchmm [in] 木構造化辞書
 * </JA>
 * <EN>
 * Adjust factoring data in tree lexicon for multipath transition handling.
 * 
 * @param wchmm [in] tree lexicon
 * </EN>
 *
 * @callgraph
 * @callergraph
 * 
 */
void
adjust_sc_index(WCHMM_INFO *wchmm)
{
  WORD_ID w;
  int i,j,k;
  HMM_Logical *ltmp;
  int ltmp_state_num;
  int ato;
  LOGPROB prob;
  int node, scid;
  A_CELL2 *ac;
  
  /* duplicate scid for HMMs with more than one arc from initial state */
  for(w=0;w<wchmm->winfo->num;w++) {
    for(k=0;k<wchmm->winfo->wlen[w];k++) {
      node = wchmm->offset[w][k];
      scid = wchmm->state[node].scid;
      if (scid == 0) continue;
      ltmp = wchmm->winfo->wseq[w][k];
      ltmp_state_num = hmm_logical_state_num(ltmp);
      if ((hmm_logical_trans(ltmp))->a[0][ltmp_state_num-1] != LOG_ZERO) {
	j = k + 1;
	if (j == wchmm->winfo->wlen[w]) {
	  if (wchmm->state[wchmm->wordend[w]].scid == 0) {
	    jlog("STAT: word %d: factoring node copied for skip phone\n", w);
	    wchmm->state[wchmm->wordend[w]].scid = scid;
	  }
	} else {
	  if (wchmm->state[wchmm->offset[w][j]].scid == 0) {
	    jlog("STAT: word %d: factoring node copied for skip phone\n", w);
	    wchmm->state[wchmm->offset[w][j]].scid = scid;
	  }
	}
      }
      for(ato=1;ato<ltmp_state_num;ato++) {
	prob = (hmm_logical_trans(ltmp))->a[0][ato];
	if (prob != LOG_ZERO) {
	  wchmm->state[node+ato-1].scid = scid;
	}
      }
    }
  }

  /* move scid and fscore on the head state to the head grammar state */
  for(i=0;i<wchmm->startnum;i++) {
    node = wchmm->startnode[i];
    if (wchmm->state[node].out.state != NULL) {
      j_internal_error("adjust_sc_index: outprob exist in word-head node??\n");
    }
    if (wchmm->next_a[node] != LOG_ZERO) {
      if (wchmm->state[node+1].scid != 0) {
	if (wchmm->state[node].scid != 0 && wchmm->state[node].scid != wchmm->state[node+1].scid) {
	  j_internal_error("adjust_sc_index: different successor list within word-head phone?\n");
	}
	wchmm->state[node].scid = wchmm->state[node+1].scid;
	wchmm->state[node+1].scid = 0;
      }
    }
    for(ac=wchmm->ac[node];ac;ac=ac->next) {
      for(k=0;k<ac->n;k++) {
	if (wchmm->state[ac->arc[k]].scid != 0) {
	  if (wchmm->state[node].scid != 0 && wchmm->state[node].scid != wchmm->state[ac->arc[k]].scid) {
	    j_internal_error("adjust_sc_index: different successor list within word-head phone?\n");
	  }
	  wchmm->state[node].scid = wchmm->state[ac->arc[k]].scid;
	  wchmm->state[ac->arc[k]].scid = 0;
	}
      }
    }
  }
}
/** 
 * <JA>
 * @brief  単語間の factoring 値のリストを返す. 
 *
 * 与えられた直前単語に対して,factoring値を計算すべき全ての単語先頭への
 * factoring 値を計算し,そのリストを返す. このfactoring値は
 * 直前単語ごとにリスト単位でキャッシュされる. すなわち,その直前単語が
 * それまでに一度でも直前単語として出現していた場合,そのリストをそのまま
 * 返す. 
 * 
 * @param wchmm [in] 木構造化辞書
 * @param lastword [in] 直前単語
 * 
 * @return 全単語先頭ノードへの factoring スコアのリスト
 * </JA>
 * <EN>
 * @brief  Compute cross-word facgtoring values for word head nodes and return
 * the list.
 *
 * Given a last word, this function compute the factoring LM scores for all
 * the word head node to which the context-dependent (not 1-gram) factoring
 * values should be computed.  The resulting list of factoring values are
 * cached within this function per the last word.
 * 
 * @param wchmm [in] tree lexicon
 * @param lastword [in] last word
 * 
 * @return the list of factoring LM scores for all the needed word-head nodes.
 * </EN>
 *
 * @callgraph
 * @callergraph
 * 
 */
LOGPROB *
max_successor_prob_iw(WCHMM_INFO *wchmm, WORD_ID lastword)
{
  int i, j, x, node;
  int last_nword;
  WORD_ID w;
  LM_PROB_CACHE *l;
  LOGPROB p;

  l = &(wchmm->lmcache);

  if (wchmm->ngram) {
    last_nword = wchmm->winfo->wton[lastword];
  } else {
    last_nword = lastword;
  }

#ifdef HASH_CACHE_IW
  x = last_nword % l->iw_cache_num;
  if (l->iw_lw_cache[x] == last_nword) { /* cache hit */
    return(l->iw_sc_cache[x]);
  }
#else  /* full cache */
  if (l->iw_sc_cache[last_nword] != NULL) { /* cache hit */
    return(l->iw_sc_cache[last_nword]);
  }
  x = last_nword;
  /* cache mis-hit, calc probs and cache them as new */
#endif
  /* allocate cache memory */
  if (l->iw_sc_cache[x] == NULL) {
#ifdef UNIGRAM_FACTORING
    l->iw_sc_cache[x] = (LOGPROB *)mymalloc(sizeof(LOGPROB)*wchmm->isolatenum);
#else
    l->iw_sc_cache[x] = (LOGPROB *)mymalloc(sizeof(LOGPROB)*wchmm->startnum);
#endif
    if (l->iw_sc_cache[x] == NULL) { /* malloc failed */
      /* clear existing cache, and retry */
      max_successor_prob_iw_free(wchmm);
      jlog("STAT: inter-word LM cache (%dMB) rehashed\n",
	       (l->iw_cache_num * 
#ifdef UNIGRAM_FACTORING
		wchmm->isolatenum
#else
		wchmm->startnum
#endif
		) / 1000 * sizeof(LOGPROB) / 1000);
#ifdef UNIGRAM_FACTORING
      l->iw_sc_cache[x] = (LOGPROB *)mymalloc(sizeof(LOGPROB)*wchmm->isolatenum);
#else
      l->iw_sc_cache[x] = (LOGPROB *)mymalloc(sizeof(LOGPROB)*wchmm->startnum);
#endif
      if (l->iw_sc_cache[x] == NULL) { /* malloc failed again? */
	j_internal_error("max_successor_prob_iw: cannot malloc\n");
      }
    }
  }

  /* calc prob for all startid */
#ifdef UNIGRAM_FACTORING
  for (j=0;j<wchmm->startnum;j++) {
    i = wchmm->start2isolate[j];
    if (i == -1) continue;
    node = wchmm->startnode[j];
    if (wchmm->state[node].scid <= 0) {
      /* should not happen!!! below is just for debugging */
      j_internal_error("max_successor_prob_iw: isolated (not shared) tree root node has unigram factoring value??\n");
    } else {
      w = wchmm->scword[wchmm->state[node].scid];
      if (wchmm->ngram) {
	p = (*(wchmm->ngram->bigram_prob))(wchmm->ngram, last_nword, wchmm->winfo->wton[w])
#ifdef CLASS_NGRAM
	  + wchmm->winfo->cprob[w]
#endif
	  ;
      } else {
	p = LOG_ZERO;
      }
      if (wchmm->lmvar == LM_NGRAM_USER) {
	p = (*(wchmm->bi_prob_user))(wchmm->winfo, lastword, w, p);
      }
      l->iw_sc_cache[x][i] = p;
    }
  }
#else  /* ~UNIGRAM_FACTORING */
  for (i=0;i<wchmm->startnum;i++) {
    node = wchmm->startnode[i];
    l->iw_sc_cache[x][i] = calc_successor_prob(wchmm, lastword, node);
  }
#endif
#ifdef HASH_CACHE_IW
  l->iw_lw_cache[x] = last_nword;
#endif

  return(l->iw_sc_cache[x]);
}
Beispiel #6
0
/** 
 * <JA>
 * 文全体のHMMを構築し,Viterbiアラインメントを実行し,結果を出力する. 
 * 
 * @param words [in] 文仮説をあらわす単語列
 * @param wnum [in] @a words の長さ
 * @param param [in] 入力特徴パラメータ列
 * @param per_what [in] 単語・音素・状態のどの単位でアラインメントを取るかを指定
 * @param align [out] アラインメント結果を格納するSentence構造体
 * @param r [i/o] 認識処理インスタンス
 * </JA>
 * <EN>
 * Build sentence HMM, call viterbi_segment() and output result.
 * 
 * @param words [in] word sequence of the sentence
 * @param wnum [in] number of words in @a words
 * @param param [in] input parameter vector
 * @param per_what [in] specify the alignment unit (word / phoneme / state)
 * @param s [out] Sentence data area to store the alignment result
 * @param r [i/o] recognition process instance
 * </EN>
 */
static void
do_align(WORD_ID *words, short wnum, HTK_Param *param, int per_what, SentenceAlign *align, RecogProcess *r)
{
  HMM_Logical **phones;		/* phoneme sequence */
  boolean *has_sp;		/* whether phone can follow short pause */
  int k;
  int phonenum;			/* num of above */
  HMM *shmm;			/* sentence HMM */
  int *end_state;		/* state number of word ends */
  int *end_frame;		/* segmented last frame of words */
  LOGPROB *end_score;		/* normalized score of each words */
  LOGPROB allscore;		/* total score of this word sequence */
  WORD_ID w;
  int i, rlen;
  int end_num = 0;
  int *id_seq, *phloc = NULL, *stloc = NULL;
  int j,n,p;
  WORD_INFO *winfo;
  HTK_HMM_INFO *hmminfo;
  boolean enable_iwsp;		/* for multipath */

  winfo = r->lm->winfo;
  hmminfo = r->am->hmminfo;
  if (hmminfo->multipath) enable_iwsp = r->lm->config->enable_iwsp;

  /* initialize result storage buffer */
  switch(per_what) {
  case PER_WORD:
    jlog("ALIGN: === word alignment begin ===\n");
    end_num = wnum;
    phloc = (int *)mymalloc(sizeof(int)*wnum);
    i = 0;
    for(w=0;w<wnum;w++) {
      phloc[w] = i;
      i += winfo->wlen[words[w]];
    }
    break;
  case PER_PHONEME:
    jlog("ALIGN: === phoneme alignment begin ===\n");
    end_num = 0;
    for(w=0;w<wnum;w++) end_num += winfo->wlen[words[w]];
    break;
  case PER_STATE:
    jlog("ALIGN: === state alignment begin ===\n");
    end_num = 0;
    for(w=0;w<wnum;w++) {
      for (i=0;i<winfo->wlen[words[w]]; i++) {
	end_num += hmm_logical_state_num(winfo->wseq[words[w]][i]) - 2;
      }
      if (hmminfo->multipath && enable_iwsp) {
	end_num += hmm_logical_state_num(hmminfo->sp) - 2;
      }
    }
    phloc = (int *)mymalloc(sizeof(int)*end_num);
    stloc = (int *)mymalloc(sizeof(int)*end_num);
    {
      n = 0;
      p = 0;
      for(w=0;w<wnum;w++) {
	for(i=0;i<winfo->wlen[words[w]]; i++) {
	  for(j=0; j<hmm_logical_state_num(winfo->wseq[words[w]][i]) - 2; j++) {
	    phloc[n] = p;
	    stloc[n] = j + 1;
	    n++;
	  }
	  if (hmminfo->multipath && enable_iwsp && i == winfo->wlen[words[w]] - 1) {
	    for(k=0;k<hmm_logical_state_num(hmminfo->sp)-2;k++) {
	      phloc[n] = p;
	      stloc[n] = j + 1 + k + end_num;
	      n++;
	    }
	  }
	  p++;
	}
      }
    }
    
    break;
  }
  end_state = (int *)mymalloc(sizeof(int) * end_num);

  /* make phoneme sequence word sequence */
  phones = make_phseq(words, wnum, &has_sp, &phonenum, &end_state, per_what, r);
  /* build the sentence HMMs */
  shmm = new_make_word_hmm(hmminfo, phones, phonenum, has_sp);
  if (shmm == NULL) {
    j_internal_error("Error: failed to make word hmm for alignment\n");
  }

  /* call viterbi segmentation function */
  allscore = viterbi_segment(shmm, param, r->wchmm->hmmwrk, hmminfo->multipath, end_state, end_num, &id_seq, &end_frame, &end_score, &rlen);

  /* store result to s */
  align->num = rlen;
  align->unittype = per_what;
  align->begin_frame = (int *)mymalloc(sizeof(int) * rlen);
  align->end_frame   = (int *)mymalloc(sizeof(int) * rlen);
  align->avgscore    = (LOGPROB *)mymalloc(sizeof(LOGPROB) * rlen);
  for(i=0;i<rlen;i++) {
    align->begin_frame[i] = (i == 0) ? 0 : end_frame[i-1] + 1;
    align->end_frame[i]   = end_frame[i];
    align->avgscore[i]    = end_score[i];
  }
  switch(per_what) {
  case PER_WORD:
    align->w = (WORD_ID *)mymalloc(sizeof(WORD_ID) * rlen);
    for(i=0;i<rlen;i++) {
      align->w[i] = words[id_seq[i]];
    }
    break;
  case PER_PHONEME:
    align->ph = (HMM_Logical **)mymalloc(sizeof(HMM_Logical *) * rlen);
    for(i=0;i<rlen;i++) {
      align->ph[i] = phones[id_seq[i]];
    }
    break;
  case PER_STATE:
    align->ph = (HMM_Logical **)mymalloc(sizeof(HMM_Logical *) * rlen);
    align->loc = (short *)mymalloc(sizeof(short) * rlen);
    if (hmminfo->multipath) align->is_iwsp = (boolean *)mymalloc(sizeof(boolean) * rlen);
    for(i=0;i<rlen;i++) {
      align->ph[i]  = phones[phloc[id_seq[i]]];
      if (hmminfo->multipath) {
	if (enable_iwsp && stloc[id_seq[i]] > end_num) {
	  align->loc[i] = stloc[id_seq[i]] - end_num;
	  align->is_iwsp[i] = TRUE;
	} else {
	  align->loc[i] = stloc[id_seq[i]];
	  align->is_iwsp[i] = FALSE;
	}
      } else {
	align->loc[i] = stloc[id_seq[i]];
      }
    }
    break;
  }

  align->allscore = allscore;

  free_hmm(shmm);
  free(id_seq);
  free(phones);
  if (has_sp) free(has_sp);
  free(end_score);
  free(end_frame);
  free(end_state);

  switch(per_what) {
  case PER_WORD:
    free(phloc);
    break;
  case PER_PHONEME:
    break;
  case PER_STATE:
    free(phloc);
    free(stloc);
  }
  
}
/** 
 * <JA>
 * 与えられた音素の並びに対して Viterbi 計算を行い,前向きスコアを
 * 更新する汎用関数. 
 * 
 * @param g [in] 現在の時間ごとの前向きスコア
 * @param g_new [out] 更新後の新たな前向きスコアを格納するバッファ
 * @param phmmseq [in] 音素HMMの並び
 * @param has_sp [in] short-pause location
 * @param phmmlen [in] @a phmmseq の長さ
 * @param param [in] 入力パラメータ
 * @param framelen [in] 入力フレーム長
 * @param least_frame [in] ビーム設定時,このフレーム数以上は Viterbi計算する
 * @param final_g [in] final g scores
 * @param wordend_frame_src [in] 現在の単語終端フレームトークン
 * @param wordend_frame_dst [out] 更新後の新たな単語終端フレームトークン
 * @param wordend_gscore_src [in] 現在の単語終端スコアトークン
 * @param wordend_gscore_dst [out] 更新後の新たな単語終端スコアトークン
 * @param r [in] recognition process instance
 * </JA>
 * <EN>
 * Generic function to perform Viterbi path updates for given phoneme
 * sequence.
 * 
 * @param g [in] current forward scores at each input frame
 * @param g_new [out] buffer to save the resulting score updates
 * @param phmmseq [in] phoneme sequence to perform Viterbi
 * @param has_sp [in] short-pause location
 * @param phmmlen [in] length of @a phmmseq.
 * @param param [in] input parameter vector
 * @param framelen [in] input frame length to compute
 * @param least_frame [in] Least frame length to force viterbi even with beam
 * @param final_g [in] final g scores
 * @param wordend_frame_src [in] current word-end frame tokens
 * @param wordend_frame_dst [out] buffer to store updated word-end frame tokens
 * @param wordend_gscore_src [in] current word-end score tokens
 * @param wordend_gscore_dst [out] buffer to store updated word-end score tokens
 * @param r [in] recognition process instance
 * 
 * </EN>
 */
static void
do_viterbi(LOGPROB *g, LOGPROB *g_new, HMM_Logical **phmmseq, boolean *has_sp, int phmmlen, HTK_Param *param, int framelen, int least_frame, LOGPROB *final_g, short *wordend_frame_src, short *wordend_frame_dst, LOGPROB *wordend_gscore_src, LOGPROB *wordend_gscore_dst, RecogProcess *r) /* has_sp and final_g is for multipath only */
{
  HMM *whmm;			/* HMM */
  int wordhmmnum;		/* length of above */
  int startt;			/* scan start frame */
  LOGPROB tmpmax,tmpscore;	/* variables for Viterbi process */
  A_CELL *ac;
  int t,i,j;
  boolean node_exist_p;
  int tn;		       ///< Temporal pointer to current buffer
  int tl;		       ///< Temporal pointer to previous buffer

  /* store global values to local for rapid access */
  StackDecode *dwrk;
  WORD_INFO *winfo;
  HTK_HMM_INFO *hmminfo;
  LOGPROB *framemaxscore;
#ifdef SCAN_BEAM
  LOGPROB scan_beam_thres;
#endif

  dwrk = &(r->pass2);
  winfo = r->lm->winfo;
  hmminfo = r->am->hmminfo;
  framemaxscore = r->pass2.framemaxscore;
#ifdef SCAN_BEAM
  scan_beam_thres = r->config->pass2.scan_beam_thres;
#endif


#ifdef TCD
  jlog("DEBUG: scan for:");
  for (i=0;i<phmmlen;i++) {
    jlog(" %s", phmmseq[i]->name);
  }
  jlog("\n");
#endif
  
  /* 単語HMMを作る */
  /* make word HMM */
  whmm = new_make_word_hmm(hmminfo, phmmseq, phmmlen, has_sp);
  if (whmm == NULL) {
    j_internal_error("Error: failed to make word hmm\n");
  }
  wordhmmnum = whmm->len;
  if (wordhmmnum >= winfo->maxwn + 10) {
    j_internal_error("do_viterbi: word too long (>%d)\n", winfo->maxwn + 10);
  }

  /* scan開始点を検索 -> starttへ*/
  /* search for the start frame -> set to startt */
  for(t = framelen-1; t >=0 ; t--) {
    if (
#ifdef SCAN_BEAM
	g[t] > framemaxscore[t] - scan_beam_thres &&
#endif
	g[t] > LOG_ZERO) {
      break;
    }
  }
  if (t < 0) {			/* no node has score > LOG_ZERO */
    /* reset all scores and end */
    for(t=0;t<framelen;t++) {
      g_new[t] = LOG_ZERO;
#ifdef GRAPHOUT_PRECISE_BOUNDARY
      if (r->graphout) {
	wordend_frame_dst[t] = -1;
	wordend_gscore_dst[t] = LOG_ZERO;
      }
#endif
    }
    free_hmm(whmm);
    return;
  }
  startt = t;
  
  /* 開始点以降[startt+1..framelen-1] の g_new[] をリセット */
  /* clear g_new[] for [startt+1..framelen-1] */
  for(t=framelen-1;t>startt;t--) {
    g_new[t] = LOG_ZERO;
#ifdef GRAPHOUT_PRECISE_BOUNDARY
    if (r->graphout) {
      wordend_frame_dst[t] = -1;
      wordend_gscore_dst[t] = LOG_ZERO;
    }
#endif
  }

  /*****************/
  /* viterbi start */
  /*****************/

  /* set initial swap buffer */
  tn = 0; tl = 1;

#ifdef GRAPHOUT_PRECISE_BOUNDARY
  if (r->graphout) {
    for(i=0;i<wordhmmnum;i++) {
      dwrk->wend_token_frame[tn][i] = -1;
      dwrk->wend_token_gscore[tn][i] = LOG_ZERO;
    }
  }
#endif

  if (! hmminfo->multipath) {
    /* 時間 [startt] 上の値を初期化 */
    /* initialize scores on frame [startt] */
    for(i=0;i<wordhmmnum-1;i++) dwrk->wordtrellis[tn][i] = LOG_ZERO;
    dwrk->wordtrellis[tn][wordhmmnum-1] = g[startt] + outprob(&(r->am->hmmwrk), startt, &(whmm->state[wordhmmnum-1]), param);
    g_new[startt] = dwrk->wordtrellis[tn][0];
#ifdef GRAPHOUT_PRECISE_BOUNDARY
    if (r->graphout) {
      dwrk->wend_token_frame[tn][wordhmmnum-1] = wordend_frame_src[startt];
      dwrk->wend_token_gscore[tn][wordhmmnum-1] = wordend_gscore_src[startt];
      wordend_frame_dst[startt] = dwrk->wend_token_frame[tn][0];
      wordend_gscore_dst[startt] = dwrk->wend_token_gscore[tn][0];
    }
#endif
  }
  
  /* メインループ: startt から始まり 0 に向かって Viterbi 計算 */
  /* main loop: start from [startt], and compute Viterbi toward [0] */
  for(t = hmminfo->multipath ? startt : startt - 1; t >= 0; t--) {
    
    /* wordtrellisのワークエリアをスワップ */
    /* swap workarea of wordtrellis */
    i = tn; tn = tl; tl = i;

    node_exist_p = FALSE;	/* TRUE if there is at least 1 survived node in this frame */

    if (! hmminfo->multipath) {
    
      /* 端のノード [t][wordhmmnum-1]は,内部遷移 か g[]の高い方になる */
      /* the edge node [t][wordhmmnum-1] is either internal transitin or g[] */
      tmpscore = LOG_ZERO;
      for (ac=whmm->state[wordhmmnum-1].ac;ac;ac=ac->next) {
	if (tmpscore < dwrk->wordtrellis[tl][ac->arc] + ac->a) {
	  j = ac->arc;
	  tmpscore = dwrk->wordtrellis[tl][ac->arc] + ac->a;
	}
      }
      if (g[t] > tmpscore) {
	tmpmax = g[t];
#ifdef GRAPHOUT_PRECISE_BOUNDARY
	if (r->graphout) {
	  dwrk->wend_token_frame[tn][wordhmmnum-1] = wordend_frame_src[t];
	  dwrk->wend_token_gscore[tn][wordhmmnum-1] = wordend_gscore_src[t];
	}
#endif
      } else {
	tmpmax = tmpscore;
#ifdef GRAPHOUT_PRECISE_BOUNDARY
	if (r->graphout) {
	  dwrk->wend_token_frame[tn][wordhmmnum-1] = dwrk->wend_token_frame[tl][j];
	  dwrk->wend_token_gscore[tn][wordhmmnum-1] = dwrk->wend_token_gscore[tl][j];
	}
#endif
      }
      
      /* 端のノードのスコアエンベロープチェック: 一定幅外なら落とす */
      /* check if the edge node is within score envelope */
      if (
#ifdef SCAN_BEAM
	  tmpmax <= framemaxscore[t] - scan_beam_thres ||
#endif
	  tmpmax <= LOG_ZERO
	  ) {
	dwrk->wordtrellis[tn][wordhmmnum-1] = LOG_ZERO;
#ifdef GRAPHOUT_PRECISE_BOUNDARY
	if (r->graphout) {
	  dwrk->wend_token_frame[tn][wordhmmnum-1] = -1;
	  dwrk->wend_token_gscore[tn][wordhmmnum-1] = LOG_ZERO;
	}
#endif
      } else {
	node_exist_p = TRUE;
	dwrk->wordtrellis[tn][wordhmmnum-1] = tmpmax + outprob(&(r->am->hmmwrk), t, &(whmm->state[wordhmmnum-1]), param);
      }

    }

    /* node[wordhmmnum-2..0]についてトレリスを展開 */
    /* expand trellis for node [t][wordhmmnum-2..0] */
    for(i=wordhmmnum-2;i>=0;i--) {
      
      /* 最尤パスと最尤スコア tmpmax を見つける */
      /* find most likely path and the max score 'tmpmax' */
      tmpmax = LOG_ZERO;
      for (ac=whmm->state[i].ac;ac;ac=ac->next) {
	if (hmminfo->multipath) {
	  if (ac->arc == wordhmmnum-1) tmpscore = g[t];
	  else if (t + 1 > startt) tmpscore = LOG_ZERO;
	  else tmpscore = dwrk->wordtrellis[tl][ac->arc];
	  tmpscore += ac->a;
	} else {
	  tmpscore = dwrk->wordtrellis[tl][ac->arc] + ac->a;
	}
	if (tmpmax < tmpscore) {
	  tmpmax = tmpscore;
	  j = ac->arc;
	}
      }
      
      /* スコアエンベロープチェック: 一定幅外なら落とす */
      /* check if score of this node is within the score envelope */
      if (
#ifdef SCAN_BEAM
	  tmpmax <= framemaxscore[t] - scan_beam_thres ||
#endif
	  tmpmax <= LOG_ZERO
	  ) {
	/* invalid node */
	dwrk->wordtrellis[tn][i] = LOG_ZERO;
#ifdef GRAPHOUT_PRECISE_BOUNDARY
	if (r->graphout) {
	  dwrk->wend_token_frame[tn][i] = -1;
	  dwrk->wend_token_gscore[tn][i] = LOG_ZERO;
	}
#endif
      } else {
	/* survived node */
	node_exist_p = TRUE;
 	dwrk->wordtrellis[tn][i] = tmpmax;
	if (! hmminfo->multipath || i > 0) {
	  dwrk->wordtrellis[tn][i] += outprob(&(r->am->hmmwrk), t, &(whmm->state[i]), param);
	}
#ifdef GRAPHOUT_PRECISE_BOUNDARY
	if (r->graphout) {
	  if (hmminfo->multipath) {
	    if (j == wordhmmnum-1) {
	      dwrk->wend_token_frame[tn][i] = wordend_frame_src[t];
	      dwrk->wend_token_gscore[tn][i] = wordend_gscore_src[t];
	    } else {
	      dwrk->wend_token_frame[tn][i] = dwrk->wend_token_frame[tl][j];
	      dwrk->wend_token_gscore[tn][i] = dwrk->wend_token_gscore[tl][j];
	    }
	  } else {
	    dwrk->wend_token_frame[tn][i] = dwrk->wend_token_frame[tl][j];
	    dwrk->wend_token_gscore[tn][i] = dwrk->wend_token_gscore[tl][j];
	  }
	}
#endif
      }
    } /* end of node loop */

    /* 時間 t のViterbi計算終了. 新たな前向きスコア g_new[t] をセット */
    /* Viterbi end for frame [t].  set the new forward score g_new[t] */
    g_new[t] = dwrk->wordtrellis[tn][0];
#ifdef GRAPHOUT_PRECISE_BOUNDARY
    if (r->graphout) {
    /* new wordend */
      wordend_frame_dst[t] = dwrk->wend_token_frame[tn][0];
      wordend_gscore_dst[t] = dwrk->wend_token_gscore[tn][0];
    }
#endif
    /* 指定された least_frame より先まで t が進んでおり,かつこの t において
       スコアエンベロープによって生き残ったノードが一つも無かった場合,
       このフレームで計算を打ち切りそれ以上先([0..t-1])は計算しない */
    /* if frame 't' already reached the 'least_frame' and no node was
       survived in this frame (all nodes pruned by score envelope),
       terminate computation at this frame and do not computer further
       frame ([0..t-1]). */
    if (t < least_frame && (!node_exist_p)) {
      /* crear the rest scores */
      for (i=t-1;i>=0;i--) {
	g_new[i] = LOG_ZERO;
#ifdef GRAPHOUT_PRECISE_BOUNDARY
	if (r->graphout) {
	  wordend_frame_dst[i] = -1;
	  wordend_gscore_dst[i] = LOG_ZERO;
	}
#endif
      }
      /* terminate loop */
      break;
    }
    
  } /* end of time loop */

  if (hmminfo->multipath) {
    /* 前向きスコアの最終値を計算 (状態 0 から時間 0 への遷移) */
    /* compute the total forward score (transition from state 0 to frame 0 */
    if (t < 0) {			/* computed till the end */
      tmpmax = LOG_ZERO;
      for(ac=whmm->state[0].ac;ac;ac=ac->next) {
	tmpscore = dwrk->wordtrellis[tn][ac->arc] + ac->a;
	if (tmpmax < tmpscore) tmpmax = tmpscore;
      }
      *final_g = tmpmax;
    } else {
      *final_g = LOG_ZERO;
    }
  }

  /* free work area */
  free_hmm(whmm);
}
/** 
* <JA>
* 最後の1単語の前向きトレリスを計算して,文仮説の前向き尤度を更新する. 
* 
* @param now [i/o] 文仮説
* @param param [in] 入力パラメータ列
* @param r [in] 認識処理インスタンス
* 
* </JA>
* <EN>
* Compute the forward viterbi for the last word to update forward scores
* and ready for word connection.
* 
* @param now [i/o] hypothesis
* @param param [in] input parameter vectors
* @param r [in] recognition process instance
* 
* </EN>
* @callgraph
* @callergraph
*/
void
scan_word(NODE *now, HTK_Param *param, RecogProcess *r)
{
  int   i,t, j;
  HMM *whmm;
  A_CELL *ac;
  WORD_ID word;
  LOGPROB tmpmax, tmptmp, score1;
  int startt = 0, endt = 0;
  int wordhmmnum;
  LOGPROB tmpmax_store, store_point_maxarc; /* multipath */
  LOGPROB tmpmax2 = LOG_ZERO;
  int phmmlen;
  HMM_Logical *ret, *wend;
  int store_point;
  int crossword_point = 0;
  boolean back_rescan = FALSE;
  boolean node_exist_p;
  int tn=0;		       ///< Temporal pointer to current buffer
  int tl=0;		       ///< Temporal pointer to previous buffer

  /* store global values to local for rapid access */
  WORD_INFO *winfo;
  HTK_HMM_INFO *hmminfo;
  LOGPROB *framemaxscore;
  int peseqlen;
  boolean ccd_flag;
  boolean enable_iwsp;
#ifdef SCAN_BEAM
  LOGPROB scan_beam_thres;
#endif
  StackDecode *dwrk;

  winfo = r->lm->winfo;
  hmminfo = r->am->hmminfo;
  dwrk = &(r->pass2);
  peseqlen = r->peseqlen;
  framemaxscore = r->pass2.framemaxscore;
  ccd_flag = r->ccd_flag;
  enable_iwsp = r->lm->config->enable_iwsp; /* multipath */
#ifdef SCAN_BEAM
  scan_beam_thres = r->config->pass2.scan_beam_thres;
#endif

  if (hmminfo->multipath) {
    store_point = -1;
  } else {
    store_point = 0;
  }

  /* ----------------------- prepare HMM ----------------------- */

  if (ccd_flag) {
    /* 直前の音素があれば,そこまでさかのぼって scan する */
    /* if there are any last phone, enable backscan */
    if (now->last_ph == NULL) {
      /* initial score: now->g[] */
      /* scan range: phones in now->seq[now->seqnum-1] */
      back_rescan = FALSE;
    } else {
      /* initial score: now->g_prev[] (1-phone before)*/
      /* scan range: phones in now->seq[now->seqnum-1] + now->last_ph */
      back_rescan = TRUE;
    }
  }
#ifdef TCD
  if (now->last_ph != NULL) {
    jlog("DEBUG: inherited last_ph: %s\n", (now->last_ph)->name);
    if (now->last_ph_sp_attached) jlog("DEBUG: (sp attached)\n"); /* multipath */
  } else {
    jlog("DEBUG: no last_ph inherited\n");
  }
#endif

  /* scan 範囲分のHMMを準備 */
  /* prepare HMM of the scan range */
  word = now->seq[now->seqnum-1];

  if (ccd_flag) {

    if (back_rescan) {

      /* scan range: phones in now->seq[now->seqnum-1] + now->last_ph */

      phmmlen = winfo->wlen[word] + 1;
      if (phmmlen > dwrk->phmmlen_max) {
        j_internal_error("scan_word: num of phonemes in a word exceed phmmlenmax (%d) ?\n", dwrk->phmmlen_max);
      }
      for (i=0;i<phmmlen - 2;i++) dwrk->phmmseq[i] = winfo->wseq[word][i];
      if (enable_iwsp && hmminfo->multipath) {
        for (i=0;i<phmmlen - 2;i++) dwrk->has_sp[i] = FALSE;
      }

      /* 最終単語と last_ph 間の単語間triphoneを考慮 */
      /* consider cross-word context dependency between the last word and now->last_ph */
      wend = winfo->wseq[word][winfo->wlen[word]-1];
      ret = get_right_context_HMM(wend, now->last_ph->name, hmminfo);
      if (ret == NULL) {	/* triphone not found */
        /* fallback to the original bi/mono-phone */
        /* error if the original is pseudo phone (not explicitly defined
        in hmmdefs/hmmlist) */
        /* exception: word with 1 phone (triphone may exist in the next expansion */
        if (winfo->wlen[word] > 1 && wend->is_pseudo) {
          error_missing_right_triphone(wend, now->last_ph->name);
        }
        dwrk->phmmseq[phmmlen-2] = wend;
      } else {
        dwrk->phmmseq[phmmlen-2] = ret;
      }
      ret = get_left_context_HMM(now->last_ph, wend->name, hmminfo);
      if (ret == NULL) {
        /* fallback to the original bi/mono-phone */
        /* error if the original is pseudo phone (not explicitly defined
        in hmmdefs/hmmlist) */
        if (now->last_ph->is_pseudo) {
          error_missing_left_triphone(now->last_ph, wend->name);
        }
        dwrk->phmmseq[phmmlen-1] = now->last_ph;
      } else {
        dwrk->phmmseq[phmmlen-1] = ret;
      }

      if (enable_iwsp && hmminfo->multipath) {
        dwrk->has_sp[phmmlen-2] = TRUE;
        dwrk->has_sp[phmmlen-1] = now->last_ph_sp_attached;
      }

#ifdef TCD
      jlog("DEBUG: w=");
      for(i=0;i<winfo->wlen[word];i++) {
        jlog(" %s",(winfo->wseq[word][i])->name);
        if (enable_iwsp && hmminfo->multipath && dwrk->has_sp[i]) jlog("(sp)");
      }
      jlog(" | %s\n", (now->last_ph)->name);
      if (hmminfo->multipath && now->last_ph_sp_attached) jlog("DEBUG:   (sp)\n");
      jlog("DEBUG: scan for:");

      for (i=0;i<phmmlen;i++) {
        jlog(" %s", dwrk->phmmseq[i]->name);
        if (enable_iwsp && hmminfo->multipath && dwrk->has_sp[i]) jlog("(sp)");
      }
      jlog("\n");
#endif

      /* 単語HMMを作る */
      /* make word HMM */
      whmm = new_make_word_hmm(hmminfo, dwrk->phmmseq, phmmlen, (enable_iwsp && hmminfo->multipath) ? dwrk->has_sp : NULL);
      if (whmm == NULL) {
        j_internal_error("Error: failed to make word hmm for word #%d \"%s [%s]\"\n", word, winfo->wname[word], winfo->woutput[word]);
      }

      /* backscan なので,計算前の g[] 初期値は now->g_prev[] を使用 */
      /* As backscan enabled, the initial forward score g[] is set by
      now->g_prev[] */
      for (t=0;t<peseqlen;t++) {
        dwrk->g[t]=now->g_prev[t];

      }

      /* 次段用のg_prevを格納するノード位置を設定 */
      /* set where to store scores as new g_prev[] for the next backscan
      in the HMM */
      if (hmminfo->multipath) {
        store_point = hmm_logical_state_num(dwrk->phmmseq[0]) - 2;
        store_point_maxarc = max_out_arc(dwrk->phmmseq[0]);
        if (enable_iwsp && dwrk->has_sp[0]) {
          store_point += hmm_logical_state_num(hmminfo->sp) - 2;
          if (store_point_maxarc < max_out_arc(hmminfo->sp)) {
            store_point_maxarc = max_out_arc(hmminfo->sp);
          }
        }
      } else {
        store_point = hmm_logical_state_num(dwrk->phmmseq[0]) - 2 - 1;
      }
      /* scan中に直前単語とこの単語をまたぐ場所を設定 */
      /* set where is the connection point of the last word in the HMM */
      if (hmminfo->multipath) {
        crossword_point = whmm->len - hmm_logical_state_num(dwrk->phmmseq[phmmlen-1]);
        if (enable_iwsp && dwrk->has_sp[phmmlen-1]) {
          crossword_point -= hmm_logical_state_num(hmminfo->sp) - 2;
        }
      } else {
        crossword_point = whmm->len - (hmm_logical_state_num(dwrk->phmmseq[phmmlen-1]) - 2) - 1;
      }

    } else {			/* not backscan mode */

      /* scan range: phones in now->seq[now->seqnum-1] */

#ifdef TCD
      jlog("DEBUG: scan(org):");
      for (i=0;i<winfo->wlen[word];i++) {
        jlog(" %s", (winfo->wseq[word][i])->name);
      }
      jlog("\n");
#endif

      if (enable_iwsp && hmminfo->multipath) {
        /* 必要ならばショートポーズを挟み込む位置を指定する */
        for(i=0;i<winfo->wlen[word];i++) {
          dwrk->has_sp[i] = FALSE;
        }
        dwrk->has_sp[winfo->wlen[word]-1] = TRUE;
      }

      /* 単語HMMを作る */
      /* make word HMM */
      whmm = new_make_word_hmm(hmminfo, winfo->wseq[word], winfo->wlen[word], (enable_iwsp && hmminfo->multipath) ? dwrk->has_sp : NULL);
      if (whmm == NULL) {
        j_internal_error("Error: failed to make word hmm for word #%d \"%s [%s]\"\n", word, winfo->wname[word], winfo->woutput[word]);
      }

      /* 計算前の g[] 初期値は now->g[] を使用 */
      /* the initial forward score g[] is set by now->g[] */
      for (t=0;t<peseqlen;t++) {
        dwrk->g[t]=now->g[t];
      }

      /* 次段用のg_prevを格納するノード位置を設定 */
      /* set where to store scores as new g_prev[] for the next backscan
      in the HMM */
      if (hmminfo->multipath) {
        store_point = hmm_logical_state_num(winfo->wseq[word][0]) - 2;
        store_point_maxarc = max_out_arc(winfo->wseq[word][0]);
        if (enable_iwsp && dwrk->has_sp[0]) {
          store_point += hmm_logical_state_num(hmminfo->sp) - 2;
          if (store_point_maxarc < max_out_arc(hmminfo->sp)) {
            store_point_maxarc = max_out_arc(hmminfo->sp);
          }
        }
      } else {
        store_point = hmm_logical_state_num(winfo->wseq[word][0]) - 2 - 1;
      }

      /* scan中に直前単語とこの単語をまたぐ場所は,なし */
      /* the connection point of the last word is not exist in the HMM */
      crossword_point = -1;
    }

  } else {			/* ccd_flag == FALSE */

    if (enable_iwsp && hmminfo->multipath) {
      /* 必要ならばショートポーズを挟み込む位置を指定する */
      for(i=0;i<winfo->wlen[word];i++) {
        dwrk->has_sp[i] = FALSE;
      }
      dwrk->has_sp[winfo->wlen[word]-1] = TRUE;
    }

    /* 音素環境非依存の場合は単純に最終単語分の HMM を作成 */
    /* for monophone: simple make HMM for the last word */
    whmm = new_make_word_hmm(hmminfo, winfo->wseq[word], winfo->wlen[word], (enable_iwsp && hmminfo->multipath) ? dwrk->has_sp : NULL);
    if (whmm == NULL) {
      j_internal_error("Error: failed to make word hmm for word #%d \"%s [%s]\"\n", word, winfo->wname[word], winfo->woutput[word]);
    }

    /* 計算前の g[] 初期値は now->g[] を使用 */
    /* the initial forward score g[] is set by now->g[] */
    for (t=0;t<peseqlen;t++) {
      dwrk->g[t]=now->g[t];
    }

  }

#ifdef TCD
  jlog("DEBUG: whmm len	  = %d\n",whmm->len);
  jlog("DEBUG: crossword_point = %d\n", crossword_point);
  jlog("DEBUG: g[] store point = %d\n", store_point);
#endif

  wordhmmnum = whmm->len;
  if (wordhmmnum >= winfo->maxwn + 10) {
    j_internal_error("scan_word: word too long (>%d)\n", winfo->maxwn + 10);
  }

#ifndef GRAPHOUT_PRECISE_BOUNDARY
  if (r->graphout) {
    if (ccd_flag) {
      now->tail_g_score = now->g[now->bestt];
    }
  }
#endif

  /* ----------------------- do scan ----------------------- */

  /* scan開始点を検索 -> starttへ*/
  /* search for the start frame -> set to startt */
  for(t = peseqlen-1; t >=0 ; t--) {
    if (
#ifdef SCAN_BEAM
      dwrk->g[t] > framemaxscore[t] - scan_beam_thres &&
#endif
      dwrk->g[t] > LOG_ZERO) {
        break;
    }
  }
  if (t < 0) {			/* no node has score > LOG_ZERO */
    for(t=0;t<peseqlen;t++) {
      if (ccd_flag) now->g_prev[t] = LOG_ZERO;
      now->g[t] = LOG_ZERO;
    }
#ifdef GRAPHOUT_PRECISE_BOUNDARY
    if (r->graphout) {
      for(t=0;t<peseqlen;t++) {
        now->wordend_frame[t] = -1;
        now->wordend_gscore[t] = LOG_ZERO;
      }
    }
#endif
    goto end_of_scan;
  }
  startt = t;

  /* clear [startt+1..peseqlen-1] */
  for(t=peseqlen-1;t>startt;t--) {
    if (ccd_flag) now->g_prev[t] = LOG_ZERO;
    now->g[t] = LOG_ZERO;
#ifdef GRAPHOUT_PRECISE_BOUNDARY
    if (r->graphout) {
      now->wordend_frame[t] = -1;
      now->wordend_gscore[t] = LOG_ZERO;
    }
#endif
  }

  /* バッファポインタ初期化 */
  tn = 0; tl = 1;

#ifdef GRAPHOUT_PRECISE_BOUNDARY
  if (r->graphout) {
    for(i=0;i<wordhmmnum;i++) {
      dwrk->wend_token_frame[tn][i] = -1;
      dwrk->wend_token_gscore[tn][i] = LOG_ZERO;
    }
  }
#endif

  if (! hmminfo->multipath) {
    /* Below initialization is not needed on multipath version, since
    the actual viterbi will begin at frame 0 in multipath mode in main loop */

    /* 時間 [startt] 上の値を初期化 */
    /* initialize scores on frame [startt] */
    for(i=0;i<wordhmmnum-1;i++) dwrk->wordtrellis[tn][i] = LOG_ZERO;
    dwrk->wordtrellis[tn][wordhmmnum-1] = dwrk->g[startt] + outprob(&(r->am->hmmwrk), startt, &(whmm->state[wordhmmnum-1]), param);
    if (ccd_flag) {
      now->g_prev[startt] = dwrk->wordtrellis[tn][store_point];
    }
    now->g[startt] = dwrk->wordtrellis[tn][0];

#ifdef GRAPHOUT_PRECISE_BOUNDARY
    if (r->graphout) {
      if (ccd_flag) {
        if (back_rescan) {
          if (wordhmmnum-1 == crossword_point) {
            dwrk->wend_token_frame[tn][wordhmmnum-1] = startt;
            dwrk->wend_token_gscore[tn][wordhmmnum-1] = dwrk->g[startt];
          } else {
            dwrk->wend_token_frame[tn][wordhmmnum-1] = -1;
            dwrk->wend_token_gscore[tn][wordhmmnum-1] = LOG_ZERO;
          }
        } else {
          dwrk->wend_token_frame[tn][wordhmmnum-1] = startt;
          dwrk->wend_token_gscore[tn][wordhmmnum-1] = dwrk->g[startt];
        }
      } else {
        dwrk->wend_token_frame[tn][wordhmmnum-1] = startt;
        dwrk->wend_token_gscore[tn][wordhmmnum-1] = dwrk->g[startt];
      }
      now->wordend_frame[startt] = dwrk->wend_token_frame[tn][0];
      now->wordend_gscore[startt] = dwrk->wend_token_gscore[tn][0];
    }
#endif
  } /* end of hmminfo->multipath */

  endt = startt;

  /* メインループ: startt から始まり 0 に向かって Viterbi 計算 */
  /* main loop: start from [startt], and compute Viterbi toward [0] */
  for(t = hmminfo->multipath ? startt : startt - 1; t >= 0; t--) {

    /* wordtrellisのワークエリアをスワップ */
    i = tn; tn = tl; tl = i;

    node_exist_p = FALSE;	/* TRUE if there is at least 1 survived node in this frame */

    if (hmminfo->multipath) {

      /* 端のノード [t][wordhmmnum-1]は g[] を参照する */
      /* the edge node [t][wordhmmnum-1] is equal to g[] */

      /* ノード [t][wordhmmnum-2..0] についてトレリスを計算 */
      /* expand trellis for node [t][wordhmmnum-2..0] */
      tmpmax_store = LOG_ZERO;

    } else {

      /* 端のノード [t][wordhmmnum-1]は,内部遷移 か g[]の高い方になる */
      /* the edge node [t][wordhmmnum-1] is either internal transitin or g[] */
      tmptmp = LOG_ZERO;
      for (ac=whmm->state[wordhmmnum-1].ac;ac;ac=ac->next) {
        score1 = dwrk->wordtrellis[tl][ac->arc] + ac->a;
        if (tmptmp < score1) {
          j = ac->arc;
          tmptmp = score1;
        }
      }
      if (dwrk->g[t] > tmptmp) {
        tmpmax = dwrk->g[t];
#ifdef GRAPHOUT_PRECISE_BOUNDARY
        if (r->graphout) {
          if (!back_rescan || wordhmmnum-1 == crossword_point) {
            dwrk->wend_token_frame[tn][wordhmmnum-1] = t;
            dwrk->wend_token_gscore[tn][wordhmmnum-1] = dwrk->g[t];
          } else {
            dwrk->wend_token_frame[tn][wordhmmnum-1] = dwrk->wend_token_frame[tl][j];
            dwrk->wend_token_gscore[tn][wordhmmnum-1] = dwrk->wend_token_gscore[tl][j];
          }
        }
#endif
      } else {
        tmpmax = tmptmp;
#ifdef GRAPHOUT_PRECISE_BOUNDARY
        if (r->graphout) {
          dwrk->wend_token_frame[tn][wordhmmnum-1] = dwrk->wend_token_frame[tl][j];
          dwrk->wend_token_gscore[tn][wordhmmnum-1] = dwrk->wend_token_gscore[tl][j];
        }
#endif
      }

      /* 端のノードのスコアエンベロープチェック: 一定幅外なら落とす */
      /* check if the edge node is within score envelope */
      if (
#ifdef SCAN_BEAM
        tmpmax <= framemaxscore[t] - scan_beam_thres ||
#endif
        tmpmax <= LOG_ZERO
        ) {
          dwrk->wordtrellis[tn][wordhmmnum-1] = LOG_ZERO;
#ifdef GRAPHOUT_PRECISE_BOUNDARY
          if (r->graphout) {
            dwrk->wend_token_frame[tn][wordhmmnum-1] = -1;
            dwrk->wend_token_gscore[tn][wordhmmnum-1] = LOG_ZERO;
          }
#endif
      } else {
        node_exist_p = TRUE;
        dwrk->wordtrellis[tn][wordhmmnum-1] = tmpmax + outprob(&(r->am->hmmwrk), t, &(whmm->state[wordhmmnum-1]), param);
      }

    } /* end of ~multipath */

    /* ノード [t][wordhmmnum-2..0] についてトレリスを計算 */
    /* expand trellis for node [t][wordhmmnum-2..0] */
    for(i=wordhmmnum-2;i>=0;i--) {

      if (ccd_flag) {

        /* 最尤パスと最尤スコア tmpmax を見つける */
        /* tmpmax2 は次回用 g_prev[] のための最大値(自己遷移を除いた最大値) */
        /* find most likely path and the max score 'tmpmax' */
        /* 'tmpmax2' is max score excluding self transition, for next g_prev[] */
        if (! hmminfo->multipath) {
          if (i == store_point) {
            tmpmax2 = LOG_ZERO;
          }
        }
        tmpmax = LOG_ZERO;
        for (ac=whmm->state[i].ac;ac;ac=ac->next) {
          if (hmminfo->multipath) {
            if (ac->arc == wordhmmnum-1) score1 = dwrk->g[t];
            else if (t + 1 > startt) score1 = LOG_ZERO;
            else score1 = dwrk->wordtrellis[tl][ac->arc];
            score1 += ac->a;
          } else {
            score1 = dwrk->wordtrellis[tl][ac->arc] + ac->a;
          }
          if (i <= crossword_point && ac->arc > crossword_point) {
            /* これは単語を越える遷移 (backscan 実行時) */
            /* this is a transition across word (when backscan is enabled) */
            score1 += now->lscore; /* add LM score */
          }

          if (hmminfo->multipath) {
            if (i <= store_point && ac->arc > store_point) {
              if (tmpmax_store < score1) tmpmax_store = score1;
            }
          } else {
            if (i == store_point && i != ac->arc) {
              if (tmpmax2 < score1) tmpmax2 = score1;
            }
          }

          if (tmpmax < score1) {
            tmpmax = score1;
            j = ac->arc;
          }
        }

        /* スコアエンベロープチェック: 一定幅外なら落とす */
        /* check if score of this node is within the score envelope */
        if (
#ifdef SCAN_BEAM
          tmpmax <= framemaxscore[t] - scan_beam_thres ||
#endif
          tmpmax <= LOG_ZERO
          ) {  /* invalid node */
            dwrk->wordtrellis[tn][i] = LOG_ZERO;
#ifdef GRAPHOUT_PRECISE_BOUNDARY
            if (r->graphout) {
              dwrk->wend_token_frame[tn][i] = -1;
              dwrk->wend_token_gscore[tn][i] = LOG_ZERO;
            }
#endif
            if (! hmminfo->multipath) {
              if (i == store_point) now->g_prev[t] = LOG_ZERO;
            }
        } else { /* survived node */
          if (! hmminfo->multipath) {
            if (i == store_point) now->g_prev[t] = tmpmax2;
          }
#ifdef GRAPHOUT_PRECISE_BOUNDARY
          if (r->graphout) {

            if (hmminfo->multipath) {
              if ((back_rescan && i <= crossword_point && j > crossword_point)
                || j == wordhmmnum-1) {
                  dwrk->wend_token_frame[tn][i] = t;
                  dwrk->wend_token_gscore[tn][i] = tmpmax;
              } else {
                dwrk->wend_token_frame[tn][i] = dwrk->wend_token_frame[tl][j];
                dwrk->wend_token_gscore[tn][i] = dwrk->wend_token_gscore[tl][j];
              }
            } else {
              if (i <= crossword_point && j > crossword_point) {
                dwrk->wend_token_frame[tn][i] = t;
                dwrk->wend_token_gscore[tn][i] = tmpmax;
              } else {
                dwrk->wend_token_frame[tn][i] = dwrk->wend_token_frame[tl][j];
                dwrk->wend_token_gscore[tn][i] = dwrk->wend_token_gscore[tl][j];
              }
            }
          }
#endif
          node_exist_p = TRUE;	/* at least one node survive in this frame */

          dwrk->wordtrellis[tn][i] = tmpmax;
          if (! hmminfo->multipath || i > 0) {
            /* compute output probability */
            dwrk->wordtrellis[tn][i] += outprob(&(r->am->hmmwrk), t, &(whmm->state[i]), param);
          }
        }

      } else {			/* not triphone */

        /* backscan 無し: store_point, crossword_point は無関係 */
        /* no backscan: store_point, crossword_point ignored */
        tmpmax = LOG_ZERO;
        if (hmminfo->multipath) {
          for (ac=whmm->state[i].ac;ac;ac=ac->next) {
            if (ac->arc == wordhmmnum-1) score1 = dwrk->g[t];
            else if (t + 1 > startt) score1 = LOG_ZERO;
            else score1 = dwrk->wordtrellis[tl][ac->arc];
            score1 += ac->a;
            if (tmpmax < score1) {
              tmpmax = score1;
              j = ac->arc;
            }
          }
        } else {
          for (ac=whmm->state[i].ac;ac;ac=ac->next) {
            score1 = dwrk->wordtrellis[tl][ac->arc] + ac->a;
            if (tmpmax < score1) {
              tmpmax = score1;
              j = ac->arc;
            }
          }
        }

        /* スコアエンベロープチェック: 一定幅外なら落とす */
        /* check if score of this node is within the score envelope */
        if (
#ifdef SCAN_BEAM
          tmpmax <= framemaxscore[t] - scan_beam_thres ||
#endif
          tmpmax <= LOG_ZERO
          ) {
            /* invalid node */
            dwrk->wordtrellis[tn][i] = LOG_ZERO;
#ifdef GRAPHOUT_PRECISE_BOUNDARY
            if (r->graphout) {
              dwrk->wend_token_frame[tn][i] = -1;
              dwrk->wend_token_gscore[tn][i] = LOG_ZERO;
            }
#endif
        } else {
          /* survived node */
          node_exist_p = TRUE;
#ifdef GRAPHOUT_PRECISE_BOUNDARY
          if (r->graphout) {
            if (hmminfo->multipath) {
              if (j == wordhmmnum-1) {
                dwrk->wend_token_frame[tn][i] = t;
                dwrk->wend_token_gscore[tn][i] = tmpmax;
              } else {
                dwrk->wend_token_frame[tn][i] = dwrk->wend_token_frame[tl][j];
                dwrk->wend_token_gscore[tn][i] = dwrk->wend_token_gscore[tl][j];
              }
            } else {
              dwrk->wend_token_frame[tn][i] = dwrk->wend_token_frame[tl][j];
              dwrk->wend_token_gscore[tn][i] = dwrk->wend_token_gscore[tl][j];
            }
          }
#endif
          /* score of node [t][i] has been determined here */
          dwrk->wordtrellis[tn][i] = tmpmax;
          if (! hmminfo->multipath || i > 0) {
            dwrk->wordtrellis[tn][i] += outprob(&(r->am->hmmwrk), t, &(whmm->state[i]), param);
          }
        }

      }
    } /* end of node loop */

    /* 時間 t のViterbi計算終了. 前向きスコアはscanした単語の始端 */
    /* Viterbi end for frame [t].  the forward score is the score of word
    beginning scanned */
    now->g[t] = dwrk->wordtrellis[tn][0];
#ifdef GRAPHOUT_PRECISE_BOUNDARY
    if (r->graphout) {
      now->wordend_frame[t] = dwrk->wend_token_frame[tn][0];
      now->wordend_gscore[t] = dwrk->wend_token_gscore[tn][0];
    }
#endif

    if (hmminfo->multipath) {
      /* triphone 時, 次段のために store_point のデータをg_prevに保存 */
      /* store the scores crossing the store_point to g_prev, for next scan */
      if (ccd_flag) {
        /* the max arc crossing the store_point always selected as tmpmax_score */ 
        tmpmax_store -= store_point_maxarc;
        if (tmpmax_store < LOG_ZERO) tmpmax_store = LOG_ZERO;
        now->g_prev[t] = tmpmax_store;
      }
    }

    /* store the number of last computed frame */
    if (node_exist_p) endt = t;

    /* scanした単語の第1パスでの始端時刻より先まで t が進んでおり,かつ
    この t においてスコアエンベロープによって生き残ったノードが一つも
    無かったならば,このフレームで計算を打ち切りそれ以上先([0..t-1])は
    計算しない */
    /* if frame 't' already reached the beginning frame of scanned word
    in 1st pass and no node was survived in this frame (all nodes pruned
    by score envelope), terminate computation at this frame and
    do not computer further frame ([0..t-1]). */
    if (t < now->estimated_next_t && (!node_exist_p)) {
      /* clear the rest scores */
      for (i=t-1;i>=0;i--) {
        now->g[i] = LOG_ZERO;
#ifdef GRAPHOUT_PRECISE_BOUNDARY
        if (r->graphout) {
          now->wordend_frame[i] = -1;
          now->wordend_gscore[i] = LOG_ZERO;
        }
#endif
        if (ccd_flag) now->g_prev[i] = LOG_ZERO;
      }
      /* terminate loop */
      break;
    }

  } /* end of time loop */

  if (debug2_flag) jlog("DEBUG: scanned: [%3d-%3d]\n", endt, startt);

end_of_scan:

  if (hmminfo->multipath) {
    /* 前向きスコアの最終値を計算 (状態 0 から時間 0 への遷移) */
    /* compute the total forward score (transition from state 0 to frame 0 */
    if (endt == 0) {
      tmpmax = LOG_ZERO;
      for(ac=whmm->state[0].ac;ac;ac=ac->next) {
        score1 = dwrk->wordtrellis[tn][ac->arc] + ac->a;
        if (tmpmax < score1) tmpmax = score1;
      }
      now->final_g = score1;
    } else {
      now->final_g = LOG_ZERO;
    }
  }

  /* 次回 backscan のための情報格納 */
  /* store data for next backscan */
  if (ccd_flag) {
    if (store_point == (hmminfo->multipath ? wordhmmnum - 2 : wordhmmnum - 1)) {
      /* last_ph無し,かつ単語の音素長=1の場合、次回の scan_word() で
      単語全体がもう一度再計算される. この場合,
      g_prev は,このscan_wordを開始する前のスコアを入れておく必要がある */
      /* if there was no 'last_ph' and the scanned word consists of only
      1 phone, the whole word should be re-computed in the future scan_word().
      So the next 'g_prev[]' should be the initial forward scores
      before we begin Viterbi (= g[t]). */
      for (t = startt; t>=0; t--) {
        now->g_prev[t] = dwrk->g[t];
      }
    }
#ifndef GRAPHOUT_PRECISE_BOUNDARY
    if (r->graphout) {
      if (now->tail_g_score != LOG_ZERO) {
        if (now->prevgraph != NULL) {
          (now->prevgraph)->leftscore = now->tail_g_score;
        }
      }
    }
#endif
    /* 次回のために now->last_ph を更新 */
    /* update 'now->last_ph' for future scan_word() */
    if (back_rescan) {
      now->last_ph = dwrk->phmmseq[0];
    } else {
      now->last_ph = winfo->wseq[word][0];
    }
    if (enable_iwsp && hmminfo->multipath) {
      now->last_ph_sp_attached = dwrk->has_sp[0];
    }
  }

#ifdef GRAPHOUT_PRECISE_BOUNDARY
  if (! hmminfo->multipath) {
    if (r->graphout) {
      /* 次回の next_word 用に境界情報を調整 */
      /* proceed word boundary for one step for next_word */
      now->wordend_frame[peseqlen-1] = now->wordend_frame[0];
      now->wordend_gscore[peseqlen-1] = now->wordend_gscore[0];
      for (t=0;t<peseqlen-1;t++) {
        now->wordend_frame[t] = now->wordend_frame[t+1];
        now->wordend_gscore[t] = now->wordend_gscore[t+1];
      }
    }
  }
#endif

  /* free work area */
  free_hmm(whmm);
#ifdef TCD
  if (hmminfo->multipath) {
    if (ccd_flag) {
      jlog("DEBUG: last_ph = %s", (now->last_ph)->name);
      if (now->last_ph_sp_attached) jlog(" (sp attached)");
      jlog("\n");
    }
  } else {
    jlog("DEBUG: last_ph = %s\n", (now->last_ph)->name);
  }
#endif
}
Beispiel #9
0
/** 
 * <JA>
 * 木構造化辞書上の状態の出力確率を計算する. 
 * 
 * @param wchmm [in] 木構造化辞書情報
 * @param node [in] ノード番号
 * @param last_wid [in] 直前単語(単語先頭のトライフォン計算に用いる)
 * @param t [in] 時間フレーム
 * @param param [in] 特徴量パラメータ構造体 (@a t 番目のベクトルについて計算する)
 * 
 * @return 出力確率の対数値を返す. 
 * </JA>
 * <EN>
 * Calculate output probability on a tree lexion node.  This function
 * calculates log output probability of an input vector on time frame @a t
 * in input paramter @a param at a node on tree lexicon.
 * 
 * @param wchmm [in] tree lexicon structure
 * @param node [in] node ID to compute the output probability
 * @param last_wid [in] word ID of last word hypothesis (used when the node is
 * within the word beginning phone and triphone is used.
 * @param t [in] time frame of input vector in @a param to compute.
 * @param param [in] input parameter structure
 * 
 * @return the computed log probability.
 * </EN>
 * @callgraph
 * @callergraph
 */
LOGPROB
outprob_style(WCHMM_INFO *wchmm, int node, int last_wid, int t, HTK_Param *param)
{
  char rbuf[MAX_HMMNAME_LEN]; ///< Local workarea for HMM name conversion

#ifndef PASS1_IWCD
  
  /* if cross-word triphone handling is disabled, we simply compute the
     output prob of the state */
  return(outprob_state(wchmm->hmmwrk, t, wchmm->state[node].out, param));
  
#else  /* PASS1_IWCD */

  /* state type and context cache is considered */
  HMM_Logical *ohmm, *rhmm;
  RC_INFO *rset;
  LRC_INFO *lrset;
  CD_Set *lcd;
  WORD_INFO *winfo = wchmm->winfo;
  HTK_HMM_INFO *hmminfo = wchmm->hmminfo;

  /* the actual computation is different according to their context dependency
     handling */
  switch(wchmm->outstyle[node]) {
  case AS_STATE:
    /* normal state (word-internal or context-independent )*/
    /* compute as usual */
    return(outprob_state(wchmm->hmmwrk, t, wchmm->state[node].out.state, param));
  case AS_LSET:
    /* node in word end phone */
    /* compute approximated value using the state set in pseudo phone */
    return(outprob_cd(wchmm->hmmwrk, t, wchmm->state[node].out.lset, param));
  case AS_RSET:
    /* note in the beginning phone of word */
    /* depends on the last word hypothesis to compute the actual triphone */
    rset = wchmm->state[node].out.rset;
    /* consult cache */
    if (rset->cache.state == NULL || rset->lastwid_cache != last_wid) {
      /* cache miss...calculate */
      /* rset contains either defined biphone or pseudo biphone */
      if (last_wid != WORD_INVALID) {
	/* lookup triphone with left-context (= last phoneme) */
	if ((ohmm = get_left_context_HMM(rset->hmm, (winfo->wseq[last_wid][winfo->wlen[last_wid]-1])->name, hmminfo)) != NULL) {
	  rhmm = ohmm;
	} else {
	  /* if triphone not found, try to use the bi-phone itself */
	  rhmm = rset->hmm;
	  /* If the bi-phone is explicitly specified in hmmdefs/HMMList,
	     use it.  if both triphone and biphone not found in user-given
	     hmmdefs/HMMList, use "pseudo" phone, as same as the end of word */
	  if (debug2_flag) {
	    if (rhmm->is_pseudo) {
	    error_missing_left_triphone(rset->hmm, (winfo->wseq[last_wid][winfo->wlen[last_wid]-1])->name);
	    }
	  }
	}
      } else {
	/* if last word is WORD_INVALID try to use the bi-phone itself */
	rhmm = rset->hmm;
	/* If the bi-phone is explicitly specified in hmmdefs/HMMList,
	   use it.  if not, use "pseudo" phone, as same as the end of word */
	if (debug2_flag) {
	  if (rhmm->is_pseudo) {
	    error_missing_left_triphone(rset->hmm, (winfo->wseq[last_wid][winfo->wlen[last_wid]-1])->name);
	  }
	}
      }
      /* rhmm may be a pseudo phone */
      /* store to cache */
      if (rhmm->is_pseudo) {
	rset->last_is_lset  = TRUE;
	rset->cache.lset    = &(rhmm->body.pseudo->stateset[rset->state_loc]);
      } else {
	rset->last_is_lset  = FALSE;
	rset->cache.state   = rhmm->body.defined->s[rset->state_loc];
      }
      rset->lastwid_cache = last_wid;
    }
    /* calculate outprob and return */
    if (rset->last_is_lset) {
      return(outprob_cd(wchmm->hmmwrk, t, rset->cache.lset, param));
    } else {
      return(outprob_state(wchmm->hmmwrk, t, rset->cache.state, param));
    }
  case AS_LRSET:
    /* node in word with only one phoneme --- both beginning and end */
    lrset = wchmm->state[node].out.lrset;
    if (lrset->cache.state == NULL || lrset->lastwid_cache != last_wid) {
      /* cache miss...calculate */
      rhmm = lrset->hmm;
      /* lookup cdset for given left context (= last phoneme) */
      strcpy(rbuf, rhmm->name);
      if (last_wid != WORD_INVALID) {
	add_left_context(rbuf, (winfo->wseq[last_wid][winfo->wlen[last_wid]-1])->name);
      }
      if (wchmm->category_tree) {
#ifdef USE_OLD_IWCD
	lcd = lcdset_lookup_by_hmmname(hmminfo, rbuf);
#else
	/* use category-indexed cdset */
	if (last_wid != WORD_INVALID &&
	    (ohmm = get_left_context_HMM(rhmm, (winfo->wseq[last_wid][winfo->wlen[last_wid]-1])->name, hmminfo)) != NULL) {
	  lcd = lcdset_lookup_with_category(wchmm, ohmm, lrset->category);
	} else {
	  lcd = lcdset_lookup_with_category(wchmm, rhmm, lrset->category);
	}
#endif
      } else {
	lcd = lcdset_lookup_by_hmmname(hmminfo, rbuf);
      }
      if (lcd != NULL) {	/* found, set to cache */
	lrset->last_is_lset  = TRUE;
        lrset->cache.lset    = &(lcd->stateset[lrset->state_loc]);
        lrset->lastwid_cache = last_wid;
      } else {
	/* no relating lcdset found, falling to normal state */
	if (rhmm->is_pseudo) {
	  lrset->last_is_lset  = TRUE;
	  lrset->cache.lset    = &(rhmm->body.pseudo->stateset[lrset->state_loc]);
	  lrset->lastwid_cache = last_wid;
	} else {
	  lrset->last_is_lset  = FALSE;
	  lrset->cache.state   = rhmm->body.defined->s[lrset->state_loc];
	  lrset->lastwid_cache = last_wid;
	}
      }
      /*printf("[%s->%s]\n", lrset->hmm->name, rhmm->name);*/
    }
    /* calculate outprob and return */
    if (lrset->last_is_lset) {
      return(outprob_cd(wchmm->hmmwrk, t, lrset->cache.lset, param));
    } else {
      return(outprob_state(wchmm->hmmwrk, t, lrset->cache.state, param));
    }
  default:
    /* should not happen */
    j_internal_error("outprob_style: no outprob style??\n");
    return(LOG_ZERO);
  }

#endif  /* PASS1_IWCD */

}