libjulius/src/search_bestfirst_v2.c

Go to the documentation of this file.
00001 
00054 /*
00055  * Copyright (c) 1991-2007 Kawahara Lab., Kyoto University
00056  * Copyright (c) 2000-2005 Shikano Lab., Nara Institute of Science and Technology
00057  * Copyright (c) 2005-2007 Julius project team, Nagoya Institute of Technology
00058  * All rights reserved
00059  */
00060 
00061 /* By "fast" setting (default), search_bestfirst_v1.c is used for faster
00062    decoding.  Please specify option "--enable-setup=standard" or
00063    "--enable-strict-iwcd2" at "./configure" to activate this. */
00064 
00065 #include <julius/julius.h>
00066 
00067 #ifdef PASS2_STRICT_IWCD
00068 
00069 #undef TCD                      
00070 
00071 
00072 /**********************************************************************/
00073 /************ 仮説ノードの基本操作                         ************/
00074 /************ Basic functions for hypothesis node handling ************/
00075 /**********************************************************************/
00076 
00077 #undef STOCKER_DEBUG
00078 
00079 #ifdef STOCKER_DEBUG
00080 static int stocked_num = 0;
00081 static int reused_num = 0;
00082 static int new_num = 0;
00083 static int request_num = 0;
00084 #endif
00085 
00098 static void
00099 free_node_exec(NODE *node)
00100 {
00101   if (node == NULL) return;
00102   free(node->g);
00103 #ifdef GRAPHOUT_PRECISE_BOUNDARY
00104   if (node->region->graphout) {
00105     free(node->wordend_frame);
00106     free(node->wordend_gscore);
00107   }
00108 #endif
00109   free(node);
00110 }
00111 
00126 void
00127 free_node(NODE *node)
00128 {
00129   if (node == NULL) return;
00130 
00131   if (node->region->graphout) {
00132     if (node->prevgraph != NULL && node->prevgraph->saved == FALSE) {
00133       wordgraph_free(node->prevgraph);
00134     }
00135   }
00136 
00137   /* save to stocker */
00138   node->next = node->region->pass2.stocker_root;
00139   node->region->pass2.stocker_root = node;
00140 
00141 #ifdef STOCKER_DEBUG
00142   stocked_num++;
00143 #endif
00144 }
00145 
00162 void
00163 clear_stocker(StackDecode *s)
00164 {
00165   NODE *node, *tmp;
00166   node = s->stocker_root;
00167   while(node) {
00168     tmp = node->next;
00169     free_node_exec(node);
00170     node = tmp;
00171   }
00172   s->stocker_root = NULL;
00173 
00174 #ifdef STOCKER_DEBUG
00175   jlog("DEBUG: %d times requested, %d times newly allocated, %d times reused\n", request_num, new_num, reused_num);
00176   stocked_num = 0;
00177   reused_num = 0;
00178   new_num = 0;
00179   request_num = 0;
00180 #endif
00181 }
00182 
00203 NODE *
00204 cpy_node(NODE *dst, NODE *src)
00205 {
00206   int peseqlen;
00207 
00208   peseqlen = src->region->peseqlen;
00209   
00210   dst->next = src->next;
00211   dst->prev = src->prev;
00212   memcpy(dst->g, src->g, sizeof(LOGPROB) * peseqlen);
00213   memcpy(dst->seq, src->seq, sizeof(WORD_ID) * MAXSEQNUM);
00214 #ifdef CM_SEARCH
00215 #ifdef CM_MULTIPLE_ALPHA
00216   {
00217     int w;
00218     for(w=0;w<src->seqnum;w++) {
00219       memcpy(dst->cmscore[w], src->cmscore[w], sizeof(LOGPROB) * src->region->config->annotate.cm_alpha_num);
00220     }
00221   }     
00222 #else
00223   memcpy(dst->cmscore, src->cmscore, sizeof(LOGPROB) * MAXSEQNUM);
00224 #endif
00225 #endif /* CM_SEARCH */
00226   dst->seqnum = src->seqnum;
00227   dst->score = src->score;
00228   dst->bestt = src->bestt;
00229   dst->estimated_next_t = src->estimated_next_t;
00230   dst->endflag = src->endflag;
00231   dst->state = src->state;
00232   dst->tre = src->tre;
00233   if (src->region->ccd_flag) {
00234     dst->last_ph = src->last_ph;
00235     dst->last_ph_sp_attached = src->last_ph_sp_attached;
00236   }
00237   dst->totallscore = src->totallscore;
00238   dst->final_g = src->final_g;
00239 #ifdef VISUALIZE
00240   dst->popnode = src->popnode;
00241 #endif
00242 
00243   if (src->region->graphout) {
00244 #ifdef GRAPHOUT_PRECISE_BOUNDARY
00245     memcpy(dst->wordend_frame, src->wordend_frame, sizeof(short) * peseqlen);
00246     memcpy(dst->wordend_gscore, src->wordend_gscore, sizeof(LOGPROB) * peseqlen);
00247 #endif
00248     dst->prevgraph = src->prevgraph;
00249     dst->lastcontext = src->lastcontext;
00250 #ifndef GRAPHOUT_PRECISE_BOUNDARY
00251     dst->tail_g_score = src->tail_g_score;
00252 #endif
00253   }
00254   return(dst);
00255 }
00256 
00277 NODE *
00278 newnode(RecogProcess *r)
00279 {
00280   NODE *tmp;
00281   int i;
00282   int peseqlen;
00283 
00284   peseqlen = r->peseqlen;
00285 
00286 #ifdef STOCKER_DEBUG
00287   request_num++;
00288 #endif
00289   if ((tmp = r->pass2.stocker_root) != NULL) {
00290     /* re-use ones in the stocker */
00291     r->pass2.stocker_root = tmp->next;
00292 #ifdef STOCKER_DEBUG
00293     stocked_num--;
00294     reused_num++;
00295 #endif
00296   } else {
00297     /* allocate new */
00298     tmp = (NODE *)mymalloc(sizeof(NODE));
00299     tmp->g = (LOGPROB *)mymalloc(sizeof(LOGPROB) * peseqlen);
00300 #ifdef GRAPHOUT_PRECISE_BOUNDARY
00301     if (r->graphout) {
00302       tmp->wordend_frame = (short *)mymalloc(sizeof(short) * peseqlen);
00303       tmp->wordend_gscore = (LOGPROB *)mymalloc(sizeof(LOGPROB) * peseqlen);
00304     }
00305 #endif
00306 #ifdef STOCKER_DEBUG
00307     new_num++;
00308 #endif
00309   }
00310 
00311   /* clear the data */
00312   /*bzero(tmp,sizeof(NODE));*/
00313   tmp->next=NULL;
00314   tmp->prev=NULL;
00315   tmp->last_ph = NULL;
00316   tmp->last_ph_sp_attached = FALSE;
00317   if (r->ccd_flag) {
00318     tmp->totallscore = LOG_ZERO;
00319   }
00320   tmp->endflag = FALSE;
00321   tmp->seqnum = 0;
00322   for(i = 0; i < peseqlen; i++) {
00323     tmp->g[i] = LOG_ZERO;
00324   }
00325   tmp->final_g = LOG_ZERO;
00326 #ifdef VISUALIZE
00327   tmp->popnode = NULL;
00328 #endif
00329   if (r->graphout) {
00330     tmp->prevgraph = NULL;
00331     tmp->lastcontext = NULL;
00332   }
00333 
00334   tmp->region = r;
00335 
00336   return(tmp);
00337 }
00338 
00339 
00340 /**********************************************************************/
00341 /************ 前向きトレリス展開と尤度計算             ****************/
00342 /************ Expand trellis and update forward score *****************/
00343 /**********************************************************************/
00344 
00345 static LOGPROB *wordtrellis[2]; 
00346 static int tn;                 
00347 static int tl;                 
00348 static LOGPROB *g;              
00349 static HMM_Logical **phmmseq;   
00350 static int phmmlen_max;         
00351 static HMM_Logical *tailph;     
00352 static boolean *has_sp;         
00353 
00354 #ifdef GRAPHOUT_PRECISE_BOUNDARY
00355 static short *wend_token_frame[2]; 
00356 static LOGPROB *wend_token_gscore[2]; 
00357 static short *wef;              
00358 static LOGPROB *wes;            
00359 #endif
00360 
00377 void
00378 malloc_wordtrellis(RecogProcess *r)
00379 {
00380   int maxwn;
00381 
00382   maxwn = r->lm->winfo->maxwn + 10;     /* CCDによる変動を考慮 */
00383   wordtrellis[0] = (LOGPROB *)mymalloc(sizeof(LOGPROB) * maxwn);
00384   wordtrellis[1] = (LOGPROB *)mymalloc(sizeof(LOGPROB) * maxwn);
00385 
00386   g = (LOGPROB *)mymalloc(sizeof(LOGPROB) * r->peseqlen);
00387 
00388   phmmlen_max = r->lm->winfo->maxwlen + 2;
00389   phmmseq = (HMM_Logical **)mymalloc(sizeof(HMM_Logical *) * phmmlen_max);
00390   if (r->am->hmminfo->multipath) {
00391     has_sp = (boolean *)mymalloc(sizeof(boolean) * phmmlen_max);
00392   } else {
00393     has_sp = NULL;
00394   }
00395 
00396   wef = NULL;
00397   wes = NULL;
00398   wend_token_frame[0] = NULL;
00399   wend_token_frame[1] = NULL;
00400   wend_token_gscore[0] = NULL;
00401   wend_token_gscore[1] = NULL;
00402 #ifdef GRAPHOUT_PRECISE_BOUNDARY
00403   if (r->graphout) {
00404     wef = (short *)mymalloc(sizeof(short) * r->peseqlen);
00405     wes = (LOGPROB *)mymalloc(sizeof(LOGPROB) * r->peseqlen);
00406     wend_token_frame[0] = (short *)mymalloc(sizeof(short) * maxwn);
00407     wend_token_frame[1] = (short *)mymalloc(sizeof(short) * maxwn);
00408     wend_token_gscore[0] = (LOGPROB *)mymalloc(sizeof(LOGPROB) * maxwn);
00409     wend_token_gscore[1] = (LOGPROB *)mymalloc(sizeof(LOGPROB) * maxwn);
00410   }
00411 #endif
00412 }
00413 
00426 void
00427 free_wordtrellis()
00428 {
00429   free(wordtrellis[0]);
00430   free(wordtrellis[1]);
00431   free(g);
00432   free(phmmseq);
00433   if (has_sp) {
00434     free(has_sp);
00435     has_sp = NULL;
00436   }
00437 #ifdef GRAPHOUT_PRECISE_BOUNDARY
00438   if (wef) {
00439     free(wef);
00440     free(wes);
00441     free(wend_token_frame[0]);
00442     free(wend_token_frame[1]);
00443     free(wend_token_gscore[0]);
00444     free(wend_token_gscore[1]);
00445     wef = NULL;
00446   }
00447 #endif
00448 }
00449 
00450 
00451 /**********************************************************************/
00452 /************ 仮説の前向き尤度計算                  *******************/
00453 /************ Compute forward score of a hypothesis *******************/
00454 /**********************************************************************/
00455 
00456 /* 与えられた音素のならび phmmseq[0..phmmlen-1]に対してviterbi計算を行う. 
00457    g[0..framelen-1] のスコアを初期値として g_new[0..framelen-1]に更新値を代入. 
00458    最低 least_frame まではscanする. */
00459 /* Viterbi computation for the given phoneme sequence 'phmmseq[0..phmmlen-1]'
00460    with g[0..framelen-1] as initial values.  The results are stored in
00461    g_new[0..framelen-1].  Scan should not terminate at least it reaches
00462    'least_frame'. */
00504 static void
00505 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 */
00506 {
00507   HMM *whmm;                    /* HMM */
00508   int wordhmmnum;               /* length of above */
00509   int startt;                   /* scan start frame */
00510   LOGPROB tmpmax,tmpscore;      /* variables for Viterbi process */
00511   A_CELL *ac;
00512   int t,i,j;
00513   boolean node_exist_p;
00514 
00515   /* store global values to local for rapid access */
00516   WORD_INFO *winfo;
00517   HTK_HMM_INFO *hmminfo;
00518   LOGPROB *framemaxscore;
00519 #ifdef SCAN_BEAM
00520   LOGPROB scan_beam_thres;
00521 #endif
00522 
00523   winfo = r->lm->winfo;
00524   hmminfo = r->am->hmminfo;
00525   framemaxscore = r->pass2.framemaxscore;
00526 #ifdef SCAN_BEAM
00527   scan_beam_thres = r->config->pass2.scan_beam_thres;
00528 #endif
00529 
00530 
00531 #ifdef TCD
00532   jlog("DEBUG: scan for:");
00533   for (i=0;i<phmmlen;i++) {
00534     jlog(" %s", phmmseq[i]->name);
00535   }
00536   jlog("\n");
00537 #endif
00538   
00539   /* 単語HMMを作る */
00540   /* make word HMM */
00541   whmm = new_make_word_hmm(hmminfo, phmmseq, phmmlen, hmminfo->multipath ? has_sp : NULL);
00542   if (whmm == NULL) {
00543     j_internal_error("Error: failed to make word hmm\n");
00544   }
00545   wordhmmnum = whmm->len;
00546   if (wordhmmnum >= winfo->maxwn + 10) {
00547     j_internal_error("do_viterbi: word too long (>%d)\n", winfo->maxwn + 10);
00548   }
00549 
00550   /* scan開始点を検索 -> starttへ*/
00551   /* search for the start frame -> set to startt */
00552   for(t = framelen-1; t >=0 ; t--) {
00553     if (
00554 #ifdef SCAN_BEAM
00555         g[t] > framemaxscore[t] - scan_beam_thres &&
00556 #endif
00557         g[t] > LOG_ZERO) {
00558       break;
00559     }
00560   }
00561   if (t < 0) {                  /* no node has score > LOG_ZERO */
00562     /* reset all scores and end */
00563     for(t=0;t<framelen;t++) {
00564       g_new[t] = LOG_ZERO;
00565 #ifdef GRAPHOUT_PRECISE_BOUNDARY
00566       if (r->graphout) {
00567         wordend_frame_dst[t] = -1;
00568         wordend_gscore_dst[t] = LOG_ZERO;
00569       }
00570 #endif
00571     }
00572     free_hmm(whmm);
00573     return;
00574   }
00575   startt = t;
00576   
00577   /* 開始点以降[startt+1..framelen-1] の g_new[] をリセット */
00578   /* clear g_new[] for [startt+1..framelen-1] */
00579   for(t=framelen-1;t>startt;t--) {
00580     g_new[t] = LOG_ZERO;
00581 #ifdef GRAPHOUT_PRECISE_BOUNDARY
00582     if (r->graphout) {
00583       wordend_frame_dst[t] = -1;
00584       wordend_gscore_dst[t] = LOG_ZERO;
00585     }
00586 #endif
00587   }
00588 
00589   /*****************/
00590   /* viterbi start */
00591   /*****************/
00592 
00593   /* set initial swap buffer */
00594   tn = 0; tl = 1;
00595 
00596 #ifdef GRAPHOUT_PRECISE_BOUNDARY
00597   if (r->graphout) {
00598     for(i=0;i<wordhmmnum;i++) {
00599       wend_token_frame[tn][i] = -1;
00600       wend_token_gscore[tn][i] = LOG_ZERO;
00601     }
00602   }
00603 #endif
00604 
00605   if (! hmminfo->multipath) {
00606     /* 時間 [startt] 上の値を初期化 */
00607     /* initialize scores on frame [startt] */
00608     for(i=0;i<wordhmmnum-1;i++) wordtrellis[tn][i] = LOG_ZERO;
00609     wordtrellis[tn][wordhmmnum-1] = g[startt] + outprob(&(r->am->hmmwrk), startt, &(whmm->state[wordhmmnum-1]), param);
00610     g_new[startt] = wordtrellis[tn][0];
00611 #ifdef GRAPHOUT_PRECISE_BOUNDARY
00612     if (r->graphout) {
00613       wend_token_frame[tn][wordhmmnum-1] = wordend_frame_src[startt];
00614       wend_token_gscore[tn][wordhmmnum-1] = wordend_gscore_src[startt];
00615       wordend_frame_dst[startt] = wend_token_frame[tn][0];
00616       wordend_gscore_dst[startt] = wend_token_gscore[tn][0];
00617     }
00618 #endif
00619   }
00620   
00621   /* メインループ: startt から始まり 0 に向かって Viterbi 計算 */
00622   /* main loop: start from [startt], and compute Viterbi toward [0] */
00623   for(t = hmminfo->multipath ? startt : startt - 1; t >= 0; t--) {
00624     
00625     /* wordtrellisのワークエリアをスワップ */
00626     /* swap workarea of wordtrellis */
00627     i = tn; tn = tl; tl = i;
00628 
00629     node_exist_p = FALSE;       /* TRUE if there is at least 1 survived node in this frame */
00630 
00631     if (! hmminfo->multipath) {
00632     
00633       /* 端のノード [t][wordhmmnum-1]は,内部遷移 か g[]の高い方になる */
00634       /* the edge node [t][wordhmmnum-1] is either internal transitin or g[] */
00635       tmpscore = LOG_ZERO;
00636       for (ac=whmm->state[wordhmmnum-1].ac;ac;ac=ac->next) {
00637         if (tmpscore < wordtrellis[tl][ac->arc] + ac->a) {
00638           j = ac->arc;
00639           tmpscore = wordtrellis[tl][ac->arc] + ac->a;
00640         }
00641       }
00642       if (g[t] > tmpscore) {
00643         tmpmax = g[t];
00644 #ifdef GRAPHOUT_PRECISE_BOUNDARY
00645         if (r->graphout) {
00646           wend_token_frame[tn][wordhmmnum-1] = wordend_frame_src[t];
00647           wend_token_gscore[tn][wordhmmnum-1] = wordend_gscore_src[t];
00648         }
00649 #endif
00650       } else {
00651         tmpmax = tmpscore;
00652 #ifdef GRAPHOUT_PRECISE_BOUNDARY
00653         if (r->graphout) {
00654           wend_token_frame[tn][wordhmmnum-1] = wend_token_frame[tl][j];
00655           wend_token_gscore[tn][wordhmmnum-1] = wend_token_gscore[tl][j];
00656         }
00657 #endif
00658       }
00659       
00660       /* 端のノードのスコアエンベロープチェック: 一定幅外なら落とす */
00661       /* check if the edge node is within score envelope */
00662       if (
00663 #ifdef SCAN_BEAM
00664           tmpmax <= framemaxscore[t] - scan_beam_thres ||
00665 #endif
00666           tmpmax <= LOG_ZERO
00667           ) {
00668         wordtrellis[tn][wordhmmnum-1] = LOG_ZERO;
00669 #ifdef GRAPHOUT_PRECISE_BOUNDARY
00670         if (r->graphout) {
00671           wend_token_frame[tn][wordhmmnum-1] = -1;
00672           wend_token_gscore[tn][wordhmmnum-1] = LOG_ZERO;
00673         }
00674 #endif
00675       } else {
00676         node_exist_p = TRUE;
00677         wordtrellis[tn][wordhmmnum-1] = tmpmax + outprob(&(r->am->hmmwrk), t, &(whmm->state[wordhmmnum-1]), param);
00678       }
00679 
00680     }
00681 
00682     /* node[wordhmmnum-2..0]についてトレリスを展開 */
00683     /* expand trellis for node [t][wordhmmnum-2..0] */
00684     for(i=wordhmmnum-2;i>=0;i--) {
00685       
00686       /* 最尤パスと最尤スコア tmpmax を見つける */
00687       /* find most likely path and the max score 'tmpmax' */
00688       tmpmax = LOG_ZERO;
00689       for (ac=whmm->state[i].ac;ac;ac=ac->next) {
00690         if (hmminfo->multipath) {
00691           if (ac->arc == wordhmmnum-1) tmpscore = g[t];
00692           else if (t + 1 > startt) tmpscore = LOG_ZERO;
00693           else tmpscore = wordtrellis[tl][ac->arc];
00694           tmpscore += ac->a;
00695         } else {
00696           tmpscore = wordtrellis[tl][ac->arc] + ac->a;
00697         }
00698         if (tmpmax < tmpscore) {
00699           tmpmax = tmpscore;
00700           j = ac->arc;
00701         }
00702       }
00703       
00704       /* スコアエンベロープチェック: 一定幅外なら落とす */
00705       /* check if score of this node is within the score envelope */
00706       if (
00707 #ifdef SCAN_BEAM
00708           tmpmax <= framemaxscore[t] - scan_beam_thres ||
00709 #endif
00710           tmpmax <= LOG_ZERO
00711           ) {
00712         /* invalid node */
00713         wordtrellis[tn][i] = LOG_ZERO;
00714 #ifdef GRAPHOUT_PRECISE_BOUNDARY
00715         if (r->graphout) {
00716           wend_token_frame[tn][i] = -1;
00717           wend_token_gscore[tn][i] = LOG_ZERO;
00718         }
00719 #endif
00720       } else {
00721         /* survived node */
00722         node_exist_p = TRUE;
00723         wordtrellis[tn][i] = tmpmax;
00724         if (! hmminfo->multipath || i > 0) {
00725           wordtrellis[tn][i] += outprob(&(r->am->hmmwrk), t, &(whmm->state[i]), param);
00726         }
00727 #ifdef GRAPHOUT_PRECISE_BOUNDARY
00728         if (r->graphout) {
00729           if (hmminfo->multipath) {
00730             if (j == wordhmmnum-1) {
00731               wend_token_frame[tn][i] = wordend_frame_src[t];
00732               wend_token_gscore[tn][i] = wordend_gscore_src[t];
00733             } else {
00734               wend_token_frame[tn][i] = wend_token_frame[tl][j];
00735               wend_token_gscore[tn][i] = wend_token_gscore[tl][j];
00736             }
00737           } else {
00738             wend_token_frame[tn][i] = wend_token_frame[tl][j];
00739             wend_token_gscore[tn][i] = wend_token_gscore[tl][j];
00740           }
00741         }
00742 #endif
00743       }
00744     } /* end of node loop */
00745 
00746     /* 時間 t のViterbi計算終了. 新たな前向きスコア g_new[t] をセット */
00747     /* Viterbi end for frame [t].  set the new forward score g_new[t] */
00748     g_new[t] = wordtrellis[tn][0];
00749 #ifdef GRAPHOUT_PRECISE_BOUNDARY
00750     if (r->graphout) {
00751     /* new wordend */
00752       wordend_frame_dst[t] = wend_token_frame[tn][0];
00753       wordend_gscore_dst[t] = wend_token_gscore[tn][0];
00754     }
00755 #endif
00756     /* 指定された least_frame より先まで t が進んでおり,かつこの t において
00757        スコアエンベロープによって生き残ったノードが一つも無かった場合,
00758        このフレームで計算を打ち切りそれ以上先([0..t-1])は計算しない */
00759     /* if frame 't' already reached the 'least_frame' and no node was
00760        survived in this frame (all nodes pruned by score envelope),
00761        terminate computation at this frame and do not computer further
00762        frame ([0..t-1]). */
00763     if (t < least_frame && (!node_exist_p)) {
00764       /* crear the rest scores */
00765       for (i=t-1;i>=0;i--) {
00766         g_new[i] = LOG_ZERO;
00767 #ifdef GRAPHOUT_PRECISE_BOUNDARY
00768         if (r->graphout) {
00769           wordend_frame_dst[i] = -1;
00770           wordend_gscore_dst[i] = LOG_ZERO;
00771         }
00772 #endif
00773       }
00774       /* terminate loop */
00775       break;
00776     }
00777     
00778   } /* end of time loop */
00779 
00780   if (hmminfo->multipath) {
00781     /* 前向きスコアの最終値を計算 (状態 0 から時間 0 への遷移) */
00782     /* compute the total forward score (transition from state 0 to frame 0 */
00783     if (t < 0) {                        /* computed till the end */
00784       tmpmax = LOG_ZERO;
00785       for(ac=whmm->state[0].ac;ac;ac=ac->next) {
00786         tmpscore = wordtrellis[tn][ac->arc] + ac->a;
00787         if (tmpmax < tmpscore) tmpmax = tmpscore;
00788       }
00789       *final_g = tmpmax;
00790     } else {
00791       *final_g = LOG_ZERO;
00792     }
00793   }
00794 
00795   /* free work area */
00796   free_hmm(whmm);
00797 }
00798 
00822 static void
00823 do_viterbi_next_word(NODE *now, NODE *new, HMM_Logical *lastphone, boolean sp, HTK_Param *param, RecogProcess *r) /* sp is for multipath only */
00824 {
00825   int t, n;
00826   LOGPROB a_value;              /* for non multi-path */
00827   int peseqlen;
00828   boolean multipath;
00829 
00830   multipath = r->am->hmminfo->multipath;
00831 
00832   peseqlen = r->peseqlen;
00833   
00834   if (! multipath) {
00835 
00836     /* もし展開元仮説の最後の単語の音素長が 1 であれば,その音素は
00837        直前の scan_word で計算されていない. この場合, now->g[] に以前の
00838        初期値が格納されている. 
00839        もし音素長が1以上であれば,now->g[] はその手前まで計算した状態
00840        のスコアが入っているので,now->g[t] から初期値を設定する必要がある */
00841     /* If the length of last word is 1, it means the last phone was not
00842        scanned in the last call of scan_word().  In this case, now->g[]
00843        keeps the previous initial value, so start viterbi with the old scores.
00844        If the length is more than 1, the now->g[] keeps the values of the
00845        scan result till the previous phone, so make initial value
00846        considering last transition probability. */
00847     if (r->lm->winfo->wlen[now->seq[now->seqnum-1]] > 1) {
00848       n = hmm_logical_state_num(lastphone);
00849       a_value = (hmm_logical_trans(lastphone))->a[n-2][n-1];
00850       for(t=0; t<peseqlen-1; t++) g[t] = now->g[t+1] + a_value;
00851       g[peseqlen-1] = LOG_ZERO;
00852     } else {
00853       for(t=0; t<peseqlen; t++) g[t] = now->g[t];
00854     }
00855 
00856   } else {
00857   
00858     for(t=0; t<peseqlen; t++) g[t] = now->g[t];
00859     phmmseq[0] = lastphone;
00860     has_sp[0] = sp;
00861 
00862   }
00863   
00864   do_viterbi(g, new->g,
00865              multipath ? phmmseq : &lastphone,
00866              multipath ? has_sp : NULL,
00867              1, param, peseqlen, now->estimated_next_t, &(new->final_g)
00868 #ifdef GRAPHOUT_PRECISE_BOUNDARY
00869              , now->wordend_frame, new->wordend_frame
00870              , now->wordend_gscore, new->wordend_gscore
00871 #else
00872              , NULL, NULL
00873              , NULL, NULL
00874 #endif
00875              , r
00876              );
00877 
00878 #ifdef GRAPHOUT_PRECISE_BOUNDARY
00879   if (! multipath) {
00880     if (r->graphout) {
00881       /* 次回の next_word 用に境界情報を調整 */
00882       /* proceed word boundary for one step for next_word */
00883       new->wordend_frame[r->peseqlen-1] = new->wordend_frame[0];
00884       new->wordend_gscore[r->peseqlen-1] = new->wordend_gscore[0];
00885       for (t=0;t<r->peseqlen-1;t++) {
00886         new->wordend_frame[t] = new->wordend_frame[t+1];
00887         new->wordend_gscore[t] = new->wordend_gscore[t+1];
00888       }
00889     }
00890   }
00891 #endif
00892 }
00893 
00913 void
00914 scan_word(NODE *now, HTK_Param *param, RecogProcess *r)
00915 {
00916   int   i,t;
00917   WORD_ID word;
00918   int phmmlen;
00919 
00920   /* store global values to local for rapid access */
00921   WORD_INFO *winfo;
00922   HTK_HMM_INFO *hmminfo;
00923   int peseqlen;
00924   boolean ccd_flag;
00925   boolean enable_iwsp;          /* multipath */
00926 
00927   winfo = r->lm->winfo;
00928   hmminfo = r->am->hmminfo;
00929   peseqlen = r->peseqlen;
00930   ccd_flag = r->ccd_flag;
00931   if (hmminfo->multipath) {
00932     enable_iwsp = r->lm->config->enable_iwsp;
00933   }
00934   
00935 #ifndef GRAPHOUT_PRECISE_BOUNDARY
00936   if (r->graphout) {
00937     if (ccd_flag) {
00938       now->tail_g_score = now->g[now->bestt];
00939     }
00940   }
00941 #endif
00942 
00943   /* ----------------------- prepare phoneme sequence ------------------ */
00944   /* triphoneなら先頭の1音素はここでは対象外(あとでnext_wordでやる) */
00945   /*             末尾の1音素はコンテキストにしたがって置換 */
00946   /* with triphone, modify the tail phone of the last word according to the
00947      previous word, and do not compute the head phone here (that will be
00948      computed later in next_word() */
00949   word = now->seq[now->seqnum-1];
00950   
00951 #ifdef TCD
00952     jlog("DEBUG: w=");
00953     for(i=0;i<winfo->wlen[word];i++) {
00954       jlog(" %s",(winfo->wseq[word][i])->name);
00955     }
00956     if (ccd_flag) {
00957       if (now->last_ph != NULL) {
00958         jlog(" | %s", (now->last_ph)->name);
00959       }
00960     }
00961     jlog("\n");
00962 #endif /* TCD */
00963     
00964   if (ccd_flag) {
00965     
00966     /* the tail triphone of the last word varies by context */
00967     if (now->last_ph != NULL) {
00968       tailph = get_right_context_HMM(winfo->wseq[word][winfo->wlen[word]-1], now->last_ph->name, hmminfo);
00969       if (tailph == NULL) {
00970         /* fallback to the original bi/mono-phone */
00971         /* error if the original is pseudo phone (not explicitly defined
00972            in hmmdefs/hmmlist) */
00973         /* exception: word with 1 phone (triphone may exist in the next expansion */
00974         if (winfo->wlen[word] > 1 && winfo->wseq[word][winfo->wlen[word]-1]->is_pseudo){
00975           error_missing_right_triphone(winfo->wseq[word][winfo->wlen[word]-1], now->last_ph->name);
00976         }
00977 
00978         tailph = winfo->wseq[word][winfo->wlen[word]-1];
00979       }
00980     } else {
00981       tailph = winfo->wseq[word][winfo->wlen[word]-1];
00982     }
00983     /* 長さ1の単語は次のnextwordでさらに変化するのでここではscanしない */
00984     /* do not scan word if the length is 1, as it further varies in the
00985        following next_word() */
00986     if (winfo->wlen[word] == 1) {
00987       now->last_ph = tailph;
00988       if (hmminfo->multipath) now->last_ph_sp_attached = TRUE;
00989 #ifdef GRAPHOUT_PRECISE_BOUNDARY
00990       if (r->graphout) {
00991         /* 単語境界伝搬情報を初期化 */
00992         /* initialize word boundary propagation info */
00993         for (t=0;t<peseqlen;t++) {
00994           now->wordend_frame[t] = t;
00995           now->wordend_gscore[t] = now->g[t];
00996         }
00997       }
00998 #endif
00999 #ifdef TCD
01000       jlog("DEBUG: suspended as %s\n", (now->last_ph)->name);
01001 #endif
01002       return;
01003     }
01004 
01005     /* scan範囲の音素列を準備 */
01006     /* prepare HMM of the scan range */
01007     phmmlen = winfo->wlen[word] - 1;
01008     if (phmmlen > phmmlen_max) {
01009       j_internal_error("scan_word: num of phonemes in a word exceed phmmlenmax (%d) ?\n", phmmlen_max);
01010     }
01011     for (i=0;i<phmmlen-1;i++) {
01012       phmmseq[i] = winfo->wseq[word][i+1];
01013     }
01014     phmmseq[phmmlen-1] = tailph;
01015     if (hmminfo->multipath) {
01016       for (i=0;i<phmmlen-1;i++) has_sp[i] = FALSE;
01017       has_sp[phmmlen-1] = (enable_iwsp) ? TRUE : FALSE;
01018     }
01019 
01020   } else {                      /* ~ccd_flag */
01021 
01022     phmmlen = winfo->wlen[word];
01023     for (i=0;i<phmmlen;i++) phmmseq[i] = winfo->wseq[word][i];
01024     if (hmminfo->multipath) {
01025       for (i=0;i<phmmlen;i++) has_sp[i] = FALSE;
01026       if (enable_iwsp) has_sp[phmmlen-1] = TRUE;
01027     }
01028 
01029   }
01030 
01031   /* 元のg[]をいったん待避しておく */
01032   /* temporally keeps the original g[] */
01033   for (t=0;t<peseqlen;t++) g[t] = now->g[t];
01034 
01035 #ifdef GRAPHOUT_PRECISE_BOUNDARY
01036   if (r->graphout) {
01037     /* 単語境界伝搬情報を初期化 */
01038     /* initialize word boundary propagation info */
01039     for (t=0;t<peseqlen;t++) {
01040       wef[t] = t;
01041       wes[t] = now->g[t];
01042     }
01043   }
01044 #endif
01045 
01046   /* viterbiを実行して g[] から now->g[] を更新する */
01047   /* do viterbi computation for phmmseq from g[] to now->g[] */
01048   do_viterbi(g, now->g, phmmseq, hmminfo->multipath ? has_sp : NULL, 
01049              phmmlen, param, peseqlen, now->estimated_next_t, &(now->final_g)
01050 #ifdef GRAPHOUT_PRECISE_BOUNDARY
01051              /* 単語境界情報 we[] から now->wordend_frame[] を更新する */
01052              /* propagate word boundary info from we[] to now->wordend_frame[] */
01053              , wef, now->wordend_frame
01054              , wes, now->wordend_gscore
01055 #else
01056              , NULL, NULL
01057              , NULL, NULL
01058 #endif
01059              , r
01060              );
01061 #ifdef GRAPHOUT_PRECISE_BOUNDARY
01062   if (! hmminfo->multipath) {
01063     if (r->graphout) {
01064       /* 次回の next_word 用に境界情報を調整 */
01065       /* proceed word boundary for one step for next_word */
01066       now->wordend_frame[peseqlen-1] = now->wordend_frame[0];
01067       now->wordend_gscore[peseqlen-1] = now->wordend_gscore[0];
01068       for (t=0;t<peseqlen-1;t++) {
01069         now->wordend_frame[t] = now->wordend_frame[t+1];
01070         now->wordend_gscore[t] = now->wordend_gscore[t+1];
01071       }
01072     }
01073   }
01074 #endif
01075 
01076   if (ccd_flag) {
01077     /* 次回のために now->last_ph を更新 */
01078     /* update 'now->last_ph' for future scan_word() */
01079     now->last_ph = winfo->wseq[word][0];
01080     if (hmminfo->multipath) now->last_ph_sp_attached = FALSE; /* wlen > 1 here */
01081 #ifdef TCD
01082     jlog("DEBUG: last_ph = %s\n", (now->last_ph)->name);
01083 #endif
01084   }
01085 }
01086 
01087 
01088 /**************************************************************************/
01089 /*** 新仮説の展開とヒューリスティックを繋いだ全体スコアを計算           ***/
01090 /*** Expand new hypothesis and compute the total score (with heuristic) ***/
01091 /**************************************************************************/
01092 
01118 void
01119 next_word(NODE *now, NODE *new, NEXTWORD *nword, HTK_Param *param, RecogProcess *r)
01120 {
01121   static HMM_Logical *lastphone, *newphone;
01122   static LOGPROB *g_src;
01123   int   t;
01124   int lastword;
01125   int   i;
01126   LOGPROB a_value;
01127   LOGPROB tmpp;
01128   int   startt;
01129   int word;
01130   TRELLIS_ATOM *tre;
01131   LOGPROB totalscore;
01132   BACKTRELLIS *backtrellis;
01133   WORD_INFO *winfo;
01134   HTK_HMM_INFO *hmminfo;
01135   int peseqlen;
01136   boolean ccd_flag;
01137 
01138   backtrellis = r->backtrellis;
01139   winfo = r->lm->winfo;
01140   hmminfo = r->am->hmminfo;
01141   peseqlen = r->peseqlen;
01142   ccd_flag = r->ccd_flag;
01143 
01144   word = nword->id;
01145   lastword = now->seq[now->seqnum-1];
01146 
01147   /* lastphone (直前単語の先頭音素) を準備 */
01148   /* prepare lastphone (head phone of previous word) */
01149   if (ccd_flag) {
01150     /* 最終音素 triphone を接続単語に会わせて変化 */
01151     /* modify triphone of last phone according to the next word */
01152     lastphone = get_left_context_HMM(now->last_ph, winfo->wseq[word][winfo->wlen[word]-1]->name, hmminfo);
01153     if (lastphone == NULL) {
01154       /* fallback to the original bi/mono-phone */
01155       /* error if the original is pseudo phone (not explicitly defined
01156          in hmmdefs/hmmlist) */
01157       /* exception: word with 1 phone (triphone may exist in the next expansion */
01158       if (now->last_ph->is_pseudo){
01159         error_missing_left_triphone(now->last_ph, winfo->wseq[word][winfo->wlen[word]-1]->name);
01160       }
01161       lastphone = now->last_ph;
01162     }
01163   }
01164 
01165   /* newphone (接続単語の末尾音素) を準備 */
01166   /* prepare newphone (tail phone of next word) */
01167   if (ccd_flag) {
01168     newphone = get_right_context_HMM(winfo->wseq[word][winfo->wlen[word]-1], now->last_ph->name, hmminfo);
01169     if (newphone == NULL) {
01170       /* fallback to the original bi/mono-phone */
01171       /* error if the original is pseudo phone (not explicitly defined
01172          in hmmdefs/hmmlist) */
01173       /* exception: word with 1 phone (triphone may exist in the next expansion */
01174       if (winfo->wlen[word] > 1 && winfo->wseq[word][winfo->wlen[word]-1]->is_pseudo){
01175         error_missing_right_triphone(winfo->wseq[word][winfo->wlen[word]-1], now->last_ph->name);
01176       }
01177       newphone = winfo->wseq[word][winfo->wlen[word]-1];
01178     }
01179   } else {
01180     newphone = winfo->wseq[word][winfo->wlen[word]-1];
01181   }
01182   
01183   /* 単語並び、DFA状態番号、言語スコアを new へ継承・更新 */
01184   /* inherit and update word sequence, DFA state and total LM score to 'new' */
01185   new->score = LOG_ZERO;
01186   for (i=0;i< now->seqnum;i++){
01187     new->seq[i] = now->seq[i];
01188 #ifdef CM_SEARCH
01189 #ifdef CM_MULTIPLE_ALPHA
01190     memcpy(new->cmscore[i], now->cmscore[i], sizeof(LOGPROB) * r->config->annotate.cm_alpha_num);
01191 #else
01192     new->cmscore[i] = now->cmscore[i];
01193 #endif
01194 #endif /* CM_SEARCH */
01195   }
01196   new->seq[i] = word;
01197   new->seqnum = now->seqnum+1;
01198   new->state = nword->next_state;
01199   new->totallscore = now->totallscore + nword->lscore;
01200   if (ccd_flag) {
01201     /* 次仮説の履歴情報として保存 */
01202     /* keep the lastphone for next scan_word() */
01203     new->last_ph = lastphone;
01204     new->last_ph_sp_attached = now->last_ph_sp_attached;
01205   }
01206 
01207   if (ccd_flag) {
01208     /* 最後の1音素(lastphone)分をscanし,更新したスコアを new に保存 */
01209     /* scan the lastphone and set the updated score to new->g[] */
01210     do_viterbi_next_word(now, new, lastphone,
01211                          hmminfo->multipath ? now->last_ph_sp_attached : FALSE,
01212                          param, r);
01213     g_src = new->g;
01214   } else {
01215     g_src = now->g;
01216 #ifdef GRAPHOUT_PRECISE_BOUNDARY
01217     if (r->graphout) {
01218       memcpy(new->wordend_frame, now->wordend_frame, sizeof(short)*peseqlen);
01219       memcpy(new->wordend_gscore, now->wordend_gscore, sizeof(LOGPROB)*peseqlen);
01220     }
01221 #endif
01222   }
01223       
01224   /* 次回の scan_word に備えて new->g[] を変更しておく */
01225   /* prepare new->g[] for next scan_word() */
01226   if (hmminfo->multipath) {
01227     startt = peseqlen-1;
01228   } else {
01229     startt = peseqlen-2;
01230   }
01231   i = hmm_logical_state_num(newphone);
01232   a_value = (hmm_logical_trans(newphone))->a[i-2][i-1];
01233   if (hmminfo->multipath) {
01234     for(t=0; t <= startt; t++) {
01235       new->g[t] = g_src[t] + nword->lscore;
01236     }
01237   } else {
01238     for(t=0; t <= startt; t++) {
01239       new->g[t] = g_src[t+1] + a_value + nword->lscore;
01240     }
01241   }
01242 
01243   /***************************************************************************/
01244   /* 前向き(第2パス),後ろ向き(第1パス)トレリスを接続し最尤接続点を見つける */
01245   /* connect forward/backward trellis to look for the best connection time   */
01246   /***************************************************************************/
01247   /*-----------------------------------------------------------------*/
01248   /* 単語トレリスを探して, 次単語の最尤接続点を発見する */
01249   /* determine the best connection time of the new word, seeking the word
01250      trellis */
01251   /*-----------------------------------------------------------------*/
01252 
01253   if (r->lmtype == LM_DFA && !r->config->pass2.looktrellis_flag) {
01254     /* すべてのフレームにわたって最尤を探す */
01255     /* search for best trellis word throughout all frame */
01256     for(t = startt; t >= 0; t--) {
01257       tre = bt_binsearch_atom(backtrellis, t, (WORD_ID) word);
01258       if (tre == NULL) continue;
01259       totalscore = new->g[t] + tre->backscore;
01260       if (! hmminfo->multipath) {
01261         if (newphone->is_pseudo) {
01262           tmpp = outprob_cd(&(r->am->hmmwrk), t, &(newphone->body.pseudo->stateset[newphone->body.pseudo->state_num-2]), param);
01263         } else {
01264           tmpp = outprob_state(&(r->am->hmmwrk), t, newphone->body.defined->s[newphone->body.defined->state_num-2], param);
01265         }
01266         totalscore += tmpp;
01267       }
01268       if (new->score < totalscore) {
01269         new->score = totalscore;
01270         new->bestt = t;
01271         new->estimated_next_t = tre->begintime - 1;
01272         new->tre = tre;
01273       }
01274     }
01275 
01276     return;
01277   }
01278 
01279   /* 最後に参照したTRELLIS_ATOMの終端時間の前後 */
01280   /* newの推定時間は,上記で採用したTRELLIS_ATOMの始端時間 */
01281 
01282   /* この展開単語のトレリス上の終端時間の前後のみスキャンする
01283      前後に連続して存在するフレームについてのみ計算 */
01284   /* search for best trellis word only around the estimated time */
01285   /* 1. search forward */
01286   for(t = (nword->tre)->endtime; t >= 0; t--) {
01287     tre = bt_binsearch_atom(backtrellis, t, (WORD_ID) word);
01288     if (tre == NULL) break;     /* go to 2 if the trellis word disappear */
01289     totalscore = new->g[t] + tre->backscore;
01290     if (! hmminfo->multipath) {
01291       if (newphone->is_pseudo) {
01292         tmpp = outprob_cd(&(r->am->hmmwrk), t, &(newphone->body.pseudo->stateset[newphone->body.pseudo->state_num-2]), param);
01293       } else {
01294         tmpp = outprob_state(&(r->am->hmmwrk), t, newphone->body.defined->s[newphone->body.defined->state_num-2], param);
01295       }
01296       totalscore += tmpp;
01297     }
01298     if (new->score < totalscore) {
01299       new->score = totalscore;
01300       new->bestt = t;
01301       new->estimated_next_t = tre->begintime - 1;
01302       new->tre = tre;
01303     }
01304   }
01305   /* 2. search bckward */
01306   for(t = (nword->tre)->endtime + 1; t <= startt; t++) {
01307     tre = bt_binsearch_atom(backtrellis, t, (WORD_ID) word);
01308     if (tre == NULL) break;     /* end if the trellis word disapper */
01309     totalscore = new->g[t] + tre->backscore;
01310     if (! hmminfo->multipath) {
01311       if (newphone->is_pseudo) {
01312         tmpp = outprob_cd(&(r->am->hmmwrk), t, &(newphone->body.pseudo->stateset[newphone->body.pseudo->state_num-2]), param);
01313       } else {
01314         tmpp = outprob_state(&(r->am->hmmwrk), t, newphone->body.defined->s[newphone->body.defined->state_num-2], param);
01315       }
01316       totalscore += tmpp;
01317     }
01318     if (new->score < totalscore) {
01319       new->score = totalscore;
01320       new->bestt = t;
01321       new->estimated_next_t = tre->begintime - 1;
01322       new->tre = tre;
01323     }
01324   }
01325 
01326   /* set current LM score */
01327   new->lscore = nword->lscore;
01328   
01329 }
01330 
01331 
01332 /**********************************************************************/
01333 /********** 初期仮説の生成                 ****************************/
01334 /********** Generate an initial hypothesis ****************************/
01335 /**********************************************************************/
01336 
01358 void
01359 start_word(NODE *new, NEXTWORD *nword, HTK_Param *param, RecogProcess *r)
01360 {
01361   HMM_Logical *newphone;
01362   WORD_ID word;
01363   TRELLIS_ATOM *tre = NULL;
01364   LOGPROB tmpp;
01365   int t;
01366 
01367   BACKTRELLIS *backtrellis;
01368   WORD_INFO *winfo;
01369 
01370   int peseqlen;
01371   boolean ccd_flag;
01372   boolean multipath;
01373 
01374   backtrellis = r->backtrellis;
01375   winfo = r->lm->winfo;
01376   peseqlen = r->peseqlen;
01377   ccd_flag = r->ccd_flag;
01378   multipath = r->am->hmminfo->multipath;
01379 
01380   /* initialize data */
01381   word = nword->id;
01382   new->score = LOG_ZERO;
01383   new->seqnum = 1;
01384   new->seq[0] = word;
01385 
01386   new->state = nword->next_state;
01387   new->totallscore = nword->lscore;
01388 
01389   /* set current LM score */
01390   new->lscore = nword->lscore;
01391 
01392   /* cross-word triphone need not be handled on startup */
01393   newphone = winfo->wseq[word][winfo->wlen[word]-1];
01394   if (ccd_flag) {
01395     new->last_ph = NULL;
01396   }
01397   
01398   if (r->lmtype == LM_PROB) {
01399     new->g[peseqlen-1] = nword->lscore;
01400   } else {
01401     new->g[peseqlen-1] = 0;
01402   }
01403   
01404   for (t=peseqlen-1; t>=0; t--) {
01405     tre = bt_binsearch_atom(backtrellis, t, word);
01406     if (tre != NULL) {
01407       if (r->graphout) {
01408         new->bestt = peseqlen-1;
01409       } else {
01410         new->bestt = t;
01411       }
01412       new->score = new->g[peseqlen-1] + tre->backscore;
01413       if (! multipath) {
01414         if (newphone->is_pseudo) {
01415           tmpp = outprob_cd(&(r->am->hmmwrk), peseqlen-1, &(newphone->body.pseudo->stateset[newphone->body.pseudo->state_num-2]), param);
01416         } else {
01417           tmpp = outprob_state(&(r->am->hmmwrk), peseqlen-1, newphone->body.defined->s[newphone->body.defined->state_num-2], param);
01418         }
01419         new->score += tmpp;
01420       }
01421       new->estimated_next_t = tre->begintime - 1;
01422       new->tre = tre;
01423       break;
01424     }
01425   }
01426   if (tre == NULL) {            /* no word in backtrellis */
01427     new->score = LOG_ZERO;
01428   }
01429 }
01430 
01452 void
01453 last_next_word(NODE *now, NODE *new, HTK_Param *param, RecogProcess *r)
01454 {
01455   cpy_node(new, now);
01456   if (r->ccd_flag) {
01457     /* 最終音素分を viterbi して最終スコアを設定 */
01458     /* scan the last phone and update the final score */
01459     if (r->am->hmminfo->multipath) {
01460       do_viterbi_next_word(now, new, now->last_ph, now->last_ph_sp_attached, param, r);
01461       new->score = new->final_g;
01462     } else {
01463       do_viterbi_next_word(now, new, now->last_ph, FALSE, param, r);
01464       new->score = new->g[0];
01465     }
01466   } else {
01467     if (r->am->hmminfo->multipath) {
01468       new->score = now->final_g;
01469     } else {
01470       new->score = now->g[0];
01471     }
01472 #ifdef GRAPHOUT_PRECISE_BOUNDARY
01473     if (r->graphout) {
01474       /* last boundary has moved to [peseqlen-1] in last scan_word() */
01475       memcpy(new->wordend_frame, now->wordend_frame, sizeof(short)*r->peseqlen);
01476       memcpy(new->wordend_gscore, now->wordend_gscore, sizeof(LOGPROB)*r->peseqlen);
01477     }
01478 #endif
01479   }
01480 }
01481 
01482 #endif /* PASS2_STRICT_IWCD */
01483 
01484 /* end of file */

Generated on Tue Dec 18 15:59:53 2007 for Julius by  doxygen 1.5.4