Julius 4.2
libjulius/src/multi-gram.c
説明を見る。
00001 
00053 /*
00054  * Copyright (c) 1991-2011 Kawahara Lab., Kyoto University
00055  * Copyright (c) 2000-2005 Shikano Lab., Nara Institute of Science and Technology
00056  * Copyright (c) 2005-2011 Julius project team, Nagoya Institute of Technology
00057  * All rights reserved
00058  */
00059 
00060 
00061 #include <julius/julius.h>
00062 
00064 #define MDEBUG
00065 
00088 static boolean
00089 multigram_rebuild_wchmm(RecogProcess *r)
00090 {
00091   boolean ret;
00092 
00093   /* re-build wchmm */
00094   if (r->wchmm != NULL) {
00095     wchmm_free(r->wchmm);
00096   }
00097   r->wchmm = wchmm_new();
00098   r->wchmm->lmtype = r->lmtype;
00099   r->wchmm->lmvar  = r->lmvar;
00100   r->wchmm->ccd_flag = r->ccd_flag;
00101   r->wchmm->category_tree = TRUE;
00102   r->wchmm->hmmwrk = &(r->am->hmmwrk);
00103   /* assign models */
00104   r->wchmm->dfa = r->lm->dfa;
00105   r->wchmm->winfo = r->lm->winfo;
00106   r->wchmm->hmminfo = r->am->hmminfo;
00107   if (r->wchmm->category_tree) {
00108     if (r->config->pass1.old_tree_function_flag) {
00109       ret = build_wchmm(r->wchmm, r->lm->config);
00110     } else {
00111       ret = build_wchmm2(r->wchmm, r->lm->config);
00112     }
00113   } else {
00114     ret = build_wchmm2(r->wchmm, r->lm->config);
00115   }
00116 
00117   /* 起動時 -check でチェックモードへ */
00118   if (r->config->sw.wchmm_check_flag) {
00119     wchmm_check_interactive(r->wchmm);
00120   }
00121   
00122   if (ret == FALSE) {
00123     jlog("ERROR: multi-gram: failed to build (global) lexicon tree for recognition\n");
00124     return FALSE;
00125   }
00126   
00127   /* guess beam width from models, when not specified */
00128   r->trellis_beam_width = set_beam_width(r->wchmm, r->config->pass1.specified_trellis_beam_width);
00129   switch(r->config->pass1.specified_trellis_beam_width) {
00130   case 0:
00131     jlog("STAT: multi-gram: beam width set to %d (full) by lexicon change\n", r->trellis_beam_width);
00132     break;
00133   case -1:
00134     jlog("STAT: multi-gram: beam width set to %d (guess) by lexicon change\n", r->trellis_beam_width);
00135   }
00136 
00137   /* re-allocate factoring cache for the tree lexicon*/
00138   /* for n-gram only?? */
00139   //max_successor_cache_free(recog->wchmm);
00140   //max_successor_cache_init(recog->wchmm);
00141 
00142   /* finished! */
00143 
00144   return TRUE;
00145 }
00146 
00172 boolean
00173 multigram_build(RecogProcess *r)
00174 {
00175   if (r->lm->winfo != NULL) {
00176     /* re-build tree lexicon for recognition process */
00177     if (multigram_rebuild_wchmm(r) == FALSE) {
00178       jlog("ERROR: multi-gram: failed to re-build tree lexicon\n");
00179       return FALSE;
00180     }
00181 #ifdef MDEBUG
00182     jlog("STAT: wchmm (re)build completed\n");
00183 #endif
00184   }
00185   return(TRUE);
00186 }
00187 
00210 static boolean
00211 multigram_append_to_global(DFA_INFO *gdfa, WORD_INFO *gwinfo, MULTIGRAM *m)
00212 {
00213   /* the new grammar 'm' will be appended to the last of gdfa and gwinfo */
00214   m->state_begin = gdfa->state_num;     /* initial state ID */
00215   m->cate_begin = gdfa->term_num;       /* initial terminal ID */
00216   m->word_begin = gwinfo->num;  /* initial word ID */
00217   
00218   /* append category ID and node number of src DFA */
00219   /* Julius allow multiple initial states: connect each initial node
00220      is not necesarry. */
00221   dfa_append(gdfa, m->dfa, m->state_begin, m->cate_begin);
00222   /* append words of src vocabulary to global winfo */
00223   if (voca_append(gwinfo, m->winfo, m->cate_begin, m->word_begin) == FALSE) {
00224     return FALSE;
00225   }
00226   /* append category->word mapping table */
00227   terminfo_append(&(gdfa->term), &(m->dfa->term), m->cate_begin, m->word_begin);
00228   /* append catergory-pair information */
00229   /* pause has already been considered on m->dfa, so just append here */
00230   if (cpair_append(gdfa, m->dfa, m->cate_begin) == FALSE) {
00231     return FALSE;
00232   }
00233   /* re-set noise entry by merging */
00234   if (dfa_pause_word_append(gdfa, m->dfa, m->cate_begin) == FALSE) {
00235     return FALSE;
00236   }
00237 
00238   jlog("STAT: Gram #%d %s: installed\n", m->id, m->name);
00239 
00240   return TRUE;
00241 }
00242 
00276 int
00277 multigram_add(DFA_INFO *dfa, WORD_INFO *winfo, char *name, PROCESS_LM *lm)
00278 {
00279   MULTIGRAM *new;
00280 
00281   /* allocate new gram */
00282   new = (MULTIGRAM *)mymalloc(sizeof(MULTIGRAM));
00283   if (name != NULL) {
00284     strncpy(new->name, name, MAXGRAMNAMELEN);
00285   } else {
00286     strncpy(new->name, "(no name)", MAXGRAMNAMELEN);
00287   }
00288 
00289   new->id = lm->gram_maxid;
00290   new->dfa = dfa;
00291   new->winfo = winfo;
00292   /* will be setup and activated after multigram_update() is called once */
00293   new->hook = MULTIGRAM_DEFAULT | MULTIGRAM_ACTIVATE;
00294   new->newbie = TRUE;           /* need to setup */
00295   new->active = FALSE;          /* default: inactive */
00296 
00297   /* the new grammar is now added to gramlist */
00298   new->next = lm->grammars;
00299   lm->grammars = new;
00300 
00301   jlog("STAT: Gram #%d %s registered\n", new->id, new->name);
00302   lm->gram_maxid++;
00303 
00304   return new->id;
00305 }
00306 
00332 boolean
00333 multigram_delete(int delid, PROCESS_LM *lm)
00334 {
00335   MULTIGRAM *m;
00336   for(m=lm->grammars;m;m=m->next) {
00337     if (m->id == delid) {
00338       m->hook |= MULTIGRAM_DELETE;
00339       jlog("STAT: Gram #%d %s: marked delete\n", m->id, m->name);
00340       break;
00341     }
00342   }
00343   if (! m) {
00344     jlog("STAT: Gram #%d: not found\n", delid);
00345     return FALSE;
00346   }
00347   return TRUE;
00348 }
00349 
00365 void
00366 multigram_delete_all(PROCESS_LM *lm)
00367 {
00368   MULTIGRAM *m;
00369   for(m=lm->grammars;m;m=m->next) {
00370     m->hook |= MULTIGRAM_DELETE;
00371   }
00372 }
00373 
00390 static boolean
00391 multigram_exec_delete(PROCESS_LM *lm)
00392 {
00393   MULTIGRAM *m, *mtmp, *mprev;
00394   boolean ret_flag = FALSE;
00395 
00396   /* exec delete */
00397   mprev = NULL;
00398   m = lm->grammars;
00399   while(m) {
00400     mtmp = m->next;
00401     if (m->hook & MULTIGRAM_DELETE) {
00402       /* if any grammar is deleted, we need to rebuild lexicons etc. */
00403       /* so tell it to the caller */
00404       if (! m->newbie) ret_flag = TRUE;
00405       if (m->dfa) dfa_info_free(m->dfa);
00406       word_info_free(m->winfo);
00407       jlog("STAT: Gram #%d %s: purged\n", m->id, m->name);
00408       free(m);
00409       if (mprev != NULL) {
00410         mprev->next = mtmp;
00411       } else {
00412         lm->grammars = mtmp;
00413       }
00414     } else {
00415       mprev = m;
00416     }
00417     m = mtmp;
00418   }
00419 
00420   return(ret_flag);
00421 }
00422 
00445 int
00446 multigram_activate(int gid, PROCESS_LM *lm)     /* only mark */
00447 {
00448   MULTIGRAM *m;
00449   int ret;
00450 
00451   for(m=lm->grammars;m;m=m->next) {
00452     if (m->id == gid) {
00453       if (m->hook & MULTIGRAM_DEACTIVATE) {
00454         ret = 0;
00455         m->hook &= ~(MULTIGRAM_DEACTIVATE);
00456         m->hook |= MULTIGRAM_ACTIVATE;
00457         jlog("STAT: Gram #%d %s: marked active, superceding deactivate\n", m->id, m->name);
00458       } else {
00459         if (m->hook & MULTIGRAM_ACTIVATE) {
00460           jlog("STAT: Gram #%d %s: already marked active\n", m->id, m->name);
00461           ret = 1;
00462         } else {
00463           ret = 0;
00464           m->hook |= MULTIGRAM_ACTIVATE;
00465           jlog("STAT: Gram #%d %s: marked activate\n", m->id, m->name);
00466         }
00467       }
00468       break;
00469     }
00470   }
00471   if (! m) {
00472     jlog("WARNING: Gram #%d: not found, activation ignored\n", gid);
00473     ret = -1;
00474   }
00475 
00476   return(ret);
00477 }
00478 
00507 int
00508 multigram_deactivate(int gid, PROCESS_LM *lm)   /* only mark */
00509 {
00510   MULTIGRAM *m;
00511   int ret;
00512 
00513   for(m=lm->grammars;m;m=m->next) {
00514     if (m->id == gid) {
00515       if (m->hook & MULTIGRAM_ACTIVATE) {
00516         ret = 0;
00517         m->hook &= ~(MULTIGRAM_ACTIVATE);
00518         m->hook |= MULTIGRAM_DEACTIVATE;
00519         jlog("STAT: Gram #%d %s: marked deactivate, superceding activate\n", m->id, m->name);
00520       } else {
00521         if (m->hook & MULTIGRAM_DEACTIVATE) {
00522           jlog("STAT: Gram #%d %s: already marked deactivate\n", m->id, m->name);
00523           ret = 1;
00524         } else {
00525           ret = 0;
00526           m->hook |= MULTIGRAM_DEACTIVATE;
00527           jlog("STAT: Gram #%d %s: marked deactivate\n", m->id, m->name);
00528         }
00529       }
00530       break;
00531     }
00532   }
00533   if (! m) {
00534     jlog("WARNING: - Gram #%d: not found, deactivation ignored\n", gid);
00535     ret = -1;
00536   }
00537 
00538   return(ret);
00539 }
00540 
00559 static boolean
00560 multigram_exec_activate(PROCESS_LM *lm)
00561 {
00562   MULTIGRAM *m;
00563   boolean modified;
00564   
00565   modified = FALSE;
00566   for(m=lm->grammars;m;m=m->next) {
00567     if (m->hook & MULTIGRAM_ACTIVATE) {
00568       m->hook &= ~(MULTIGRAM_ACTIVATE);
00569       if (!m->active) {
00570         jlog("STAT: Gram #%d %s: turn on active\n", m->id, m->name);
00571       }
00572       m->active = TRUE;
00573       modified = TRUE;
00574     } else if (m->hook & MULTIGRAM_DEACTIVATE) {
00575       m->hook &= ~(MULTIGRAM_DEACTIVATE);
00576       if (m->active) {
00577         jlog("STAT: Gram #%d %s: turn off inactive\n", m->id, m->name);
00578       }
00579       m->active = FALSE;
00580       modified = TRUE;
00581     }
00582   }
00583   return(modified);
00584 }
00585  
00619 boolean                         /* return FALSE if no gram */
00620 multigram_update(PROCESS_LM *lm)
00621 {
00622   MULTIGRAM *m;
00623   boolean active_changed = FALSE;
00624   boolean rebuild_flag;
00625 
00626   if (lm->lmvar == LM_DFA_GRAMMAR) {
00627     /* setup additional grammar info of new ones */
00628     for(m=lm->grammars;m;m=m->next) {
00629       if (m->newbie) {
00630         jlog("STAT: Gram #%d %s: new grammar loaded, now mash it up for recognition\n", m->id, m->name);
00631         /* map dict item to dfa terminal symbols */
00632         if (make_dfa_voca_ref(m->dfa, m->winfo) == FALSE) {
00633           jlog("ERROR: failed to map dict <-> DFA. This grammar will be deleted\n");
00634           /* mark as to be deleted */
00635           m->hook |= MULTIGRAM_DELETE;
00636           continue;
00637         } 
00638         /* set dfa->sp_id and dfa->is_sp */
00639         dfa_find_pause_word(m->dfa, m->winfo, lm->am->hmminfo);
00640         /* build catergory-pair information */
00641         jlog("STAT: Gram #%d %s: extracting category-pair constraint for the 1st pass\n", m->id, m->name);
00642         if (extract_cpair(m->dfa) == FALSE) {
00643           jlog("ERROR: failed to extract category pair. This grammar will be deleted\n");
00644           /* mark as to be deleted */
00645           m->hook |= MULTIGRAM_DELETE;
00646         }
00647       }
00648     }
00649   }
00650 
00651   rebuild_flag = FALSE;
00652   /* delete grammars marked as "delete" */
00653   if (multigram_exec_delete(lm)) { /* some built grammars deleted */
00654     rebuild_flag = TRUE;        /* needs rebuilding global grammar */
00655   }
00656   /* find modified grammar */
00657   for(m=lm->grammars;m;m=m->next) {
00658     if (m->hook & MULTIGRAM_MODIFIED) {
00659       rebuild_flag = TRUE;      /* needs rebuilding global grammar */
00660       m->hook &= ~(MULTIGRAM_MODIFIED);
00661     }
00662   }
00663 
00664   if (rebuild_flag) {
00665     /* rebuild global grammar from scratch (including new) */
00666     /* active status not changed here (inactive grammar will also included) */
00667     /* activate/deactivate hook will be handled later, so just keep it here */
00668 #ifdef MDEBUG
00669     jlog("STAT: re-build whole global grammar...\n");
00670 #endif
00671     /* free old if not yet */
00672     if (lm->dfa != NULL) {
00673       dfa_info_free(lm->dfa);
00674       lm->dfa = NULL;
00675     }
00676     if (lm->winfo != NULL) {
00677       word_info_free(lm->winfo);
00678       lm->winfo = NULL;
00679     }
00680     /* concatinate all existing grammars to global */
00681     for(m=lm->grammars;m;m=m->next) {
00682       if (lm->lmvar == LM_DFA_GRAMMAR && lm->dfa == NULL) {
00683         lm->dfa = dfa_info_new();
00684         dfa_state_init(lm->dfa);
00685       }
00686       if (lm->winfo == NULL) {
00687         lm->winfo = word_info_new();
00688         winfo_init(lm->winfo);
00689       }
00690       if (m->newbie) m->newbie = FALSE;
00691       if (lm->lmvar == LM_DFA_WORD) {
00692         /* just append dictionaty */
00693         m->word_begin = lm->winfo->num;
00694         if (voca_append(lm->winfo, m->winfo, m->id, m->word_begin) == FALSE) {
00695           jlog("ERROR: multi-gram: failed to add dictionary #%d to recognition network\n", m->id);
00696           /* mark as delete */
00697           m->hook |= MULTIGRAM_DELETE;
00698         }
00699       } else {
00700         if (multigram_append_to_global(lm->dfa, lm->winfo, m) == FALSE) {
00701           jlog("ERROR: multi-gram: failed to add grammar #%d to recognition network\n", m->id);
00702           /* mark as delete */
00703           m->hook |= MULTIGRAM_DELETE;
00704         }
00705       }
00706     }
00707     /* delete the error grammars if exist */
00708     if (multigram_exec_delete(lm)) {
00709       jlog("ERROR: errorous grammar deleted\n");
00710     }
00711     lm->global_modified = TRUE;
00712   } else {                      /* global not need changed by the deletion */
00713     /* append only new grammars */
00714     for(m=lm->grammars;m;m=m->next) {
00715       if (m->newbie) {
00716         if (lm->lmvar == LM_DFA_GRAMMAR && lm->dfa == NULL) {
00717           lm->dfa = dfa_info_new();
00718           dfa_state_init(lm->dfa);
00719         }
00720         if (lm->winfo == NULL) {
00721           lm->winfo = word_info_new();
00722           winfo_init(lm->winfo);
00723         }
00724         if (m->newbie) m->newbie = FALSE;
00725         if (lm->lmvar == LM_DFA_WORD) {
00726           /* just append dictionaty */
00727           m->word_begin = lm->winfo->num;
00728           if (voca_append(lm->winfo, m->winfo, m->id, m->word_begin) == FALSE) {
00729             jlog("ERROR: multi-gram: failed to add dictionary #%d to recognition network\n", m->id);
00730             /* mark as delete */
00731             m->hook |= MULTIGRAM_DELETE;
00732           }
00733         } else {
00734           if (multigram_append_to_global(lm->dfa, lm->winfo, m) == FALSE) {
00735             jlog("ERROR: multi-gram: failed to add grammar #%d to recognition network\n", m->id);
00736             /* mark as delete */
00737             m->hook |= MULTIGRAM_DELETE;
00738           }
00739         }
00740         lm->global_modified = TRUE;
00741       }
00742     }
00743   }
00744 
00745   /* process activate/deactivate hook */
00746   active_changed = multigram_exec_activate(lm);
00747 
00748   if (lm->global_modified) {            /* if global lexicon has changed */
00749     /* now global grammar info has been updated */
00750     /* check if no grammar */
00751     if (lm->lmvar == LM_DFA_GRAMMAR) {
00752       if (lm->dfa == NULL || lm->winfo == NULL) {
00753         if (lm->dfa != NULL) {
00754           dfa_info_free(lm->dfa);
00755           lm->dfa = NULL;
00756         }
00757         if (lm->winfo != NULL) {
00758           word_info_free(lm->winfo);
00759           lm->winfo = NULL;
00760         }
00761       }
00762     }
00763 #ifdef MDEBUG
00764     jlog("STAT: grammar update completed\n");
00765 #endif
00766   }
00767 
00768   if (lm->global_modified || active_changed) {
00769     return (TRUE);
00770   }
00771 
00772   return FALSE;
00773 }
00774 
00791 static boolean
00792 multigram_read_file_and_add(char *dfa_file, char *dict_file, PROCESS_LM *lm)
00793 {
00794   WORD_INFO *new_winfo;
00795   DFA_INFO *new_dfa;
00796   char buf[MAXGRAMNAMELEN], *p, *q;
00797   boolean ret;
00798 
00799   if (dfa_file != NULL) {
00800     jlog("STAT: reading [%s] and [%s]...\n", dfa_file, dict_file);
00801   } else {
00802     jlog("STAT: reading [%s]...\n", dict_file);
00803   }
00804   
00805   /* read dict*/
00806   new_winfo = word_info_new();
00807 
00808   if (lm->lmvar == LM_DFA_GRAMMAR) {
00809     ret = init_voca(new_winfo, dict_file, lm->am->hmminfo, 
00810 #ifdef MONOTREE
00811                     TRUE,
00812 #else 
00813                     FALSE,
00814 #endif
00815                     lm->config->forcedict_flag);
00816     if ( ! ret ) {
00817       jlog("ERROR: failed to read dictionary \"%s\"\n", dict_file);
00818       word_info_free(new_winfo);
00819       return FALSE;
00820     }
00821   } else if (lm->lmvar == LM_DFA_WORD) {
00822     ret = init_wordlist(new_winfo, dict_file, lm->am->hmminfo, 
00823                         lm->config->wordrecog_head_silence_model_name,
00824                         lm->config->wordrecog_tail_silence_model_name,
00825                         (lm->config->wordrecog_silence_context_name[0] == '\0') ? NULL : lm->config->wordrecog_silence_context_name,
00826                         lm->config->forcedict_flag);
00827     if ( ! ret ) {
00828       jlog("ERROR: failed to read word list \"%s\"\n", dict_file);
00829       word_info_free(new_winfo);
00830       return FALSE;
00831     }
00832   }
00833 
00834   new_dfa = NULL;
00835   if (lm->lmvar == LM_DFA_GRAMMAR) {
00836     /* read dfa */
00837     new_dfa = dfa_info_new();
00838     if (init_dfa(new_dfa, dfa_file) == FALSE) {
00839       jlog("ERROR: multi-gram: error in reading DFA\n");
00840       word_info_free(new_winfo);
00841       dfa_info_free(new_dfa);
00842       return FALSE;
00843     }
00844   }
00845 
00846   jlog("STAT: done\n");
00847 
00848   /* extract name */
00849   p = &(dict_file[0]);
00850   q = p;
00851   while(*p != '\0') {
00852     if (*p == '/') q = p + 1;
00853     p++;
00854   }
00855   p = q;
00856   while(*p != '\0' && *p != '.') {
00857     buf[p-q] = *p;
00858     p++;
00859   }
00860   buf[p-q] = '\0';
00861   
00862   /* register the new grammar to multi-gram tree */
00863   multigram_add(new_dfa, new_winfo, buf, lm);
00864 
00865   return TRUE;
00866 
00867 }
00868 
00869 
00886 boolean
00887 multigram_load_all_gramlist(PROCESS_LM *lm)
00888 {
00889   GRAMLIST *g;
00890   GRAMLIST *groot;
00891   boolean ok_p;
00892 
00893   switch(lm->config->lmvar) {
00894   case LM_DFA_GRAMMAR: groot = lm->config->gramlist_root; break;
00895   case LM_DFA_WORD:    groot = lm->config->wordlist_root; break;
00896   }
00897 
00898   ok_p = TRUE;
00899   for(g = groot; g; g = g->next) {
00900     if (multigram_read_file_and_add(g->dfafile, g->dictfile, lm) == FALSE) {
00901       ok_p = FALSE;
00902     }
00903   }
00904   return(ok_p);
00905 }
00906 
00926 int
00927 multigram_get_all_num(PROCESS_LM *lm)
00928 {
00929   MULTIGRAM *m;
00930   int cnt;
00931   
00932   cnt = 0;
00933   for(m=lm->grammars;m;m=m->next) cnt++;
00934   return(cnt);
00935 }
00936 
00958 int
00959 multigram_get_gram_from_category(int category, PROCESS_LM *lm)
00960 {
00961   MULTIGRAM *m;
00962   int tb, te;
00963   for(m = lm->grammars; m; m = m->next) {
00964     if (m->newbie) continue;
00965     tb = m->cate_begin;
00966     te = tb + m->dfa->term_num;
00967     if (tb <= category && category < te) { /* found */
00968       return(m->id);
00969     }
00970   }
00971   return(-1);
00972 }
00973 
00995 int
00996 multigram_get_gram_from_wid(WORD_ID wid, PROCESS_LM *lm)
00997 {
00998   MULTIGRAM *m;
00999   int wb, we;
01000 
01001   for(m = lm->grammars; m; m = m->next) {
01002     if (m->newbie) continue;
01003     wb = m->word_begin;
01004     we = wb + m->winfo->num;
01005     if (wb <= wid && wid < we) { /* found */
01006       return(m->id);
01007     }
01008   }
01009   return(-1);
01010 }
01011 
01012 
01027 void
01028 multigram_free_all(MULTIGRAM *root)
01029 {
01030   MULTIGRAM *m, *mtmp;
01031 
01032   m = root;
01033   while(m) {
01034     mtmp = m->next;
01035     if (m->dfa) dfa_info_free(m->dfa);
01036     word_info_free(m->winfo);
01037     free(m);
01038     m = mtmp;
01039   }
01040 }
01041 
01060 int
01061 multigram_get_id_by_name(PROCESS_LM *lm, char *gramname)
01062 {
01063   MULTIGRAM *m;
01064 
01065   for(m=lm->grammars;m;m=m->next) {
01066     if (strmatch(m->name, gramname)) break;
01067   }
01068   if (!m) {
01069     jlog("ERROR: multigram: cannot find grammar \"%s\"\n", gramname);
01070     return -1;
01071   }
01072   return m->id;
01073 }
01074 
01093 MULTIGRAM *
01094 multigram_get_grammar_by_name(PROCESS_LM *lm, char *gramname)
01095 {
01096   MULTIGRAM *m;
01097 
01098   for(m=lm->grammars;m;m=m->next) {
01099     if (strmatch(m->name, gramname)) break;
01100   }
01101   if (!m) {
01102     jlog("ERROR: multigram: cannot find grammar \"%s\"\n", gramname);
01103     return NULL;
01104   }
01105   return m;
01106 }
01107 
01126 MULTIGRAM *
01127 multigram_get_grammar_by_id(PROCESS_LM *lm, unsigned short id)
01128 {
01129   MULTIGRAM *m;
01130 
01131   for(m=lm->grammars;m;m=m->next) {
01132     if (m->id == id) break;
01133   }
01134   if (!m) {
01135     jlog("ERROR: multi-gram: cannot find grammar id \"%d\"\n", id);
01136     return NULL;
01137   }
01138   return m;
01139 }
01140 
01175 boolean
01176 multigram_add_words_to_grammar(PROCESS_LM *lm, MULTIGRAM *m, WORD_INFO *winfo)
01177 {
01178   int offset;
01179 
01180   if (lm == NULL || m == NULL || winfo == NULL) return FALSE;
01181 
01182   offset = m->winfo->num;
01183   printf("adding %d words to grammar #%d (%d words)\n", winfo->num, m->id, m->winfo->num);
01184   /* append to the grammar */
01185   if (voca_append(m->winfo, winfo, m->id, offset) == FALSE) {
01186     jlog("ERROR: multi-gram: failed to add words to dict in grammar #%d \"%s\"\n", m->id, m->name);
01187     return FALSE;
01188   }
01189   /* update dictianary info */
01190   if (lm->lmvar == LM_DFA_GRAMMAR) {
01191     if (m->dfa->term_num != 0) free_terminfo(&(m->dfa->term));
01192     if (make_dfa_voca_ref(m->dfa, m->winfo) == FALSE) {
01193       jlog("ERROR: failed to map dict <-> DFA. This grammar will be deleted\n");
01194       return FALSE;
01195     } 
01196   }
01197   /* prepare for update */
01198   m->hook |= MULTIGRAM_MODIFIED;
01199 
01200   return TRUE;
01201 }
01202 
01228 boolean
01229 multigram_add_words_to_grammar_by_name(PROCESS_LM *lm, char *gramname, WORD_INFO *winfo)
01230 {
01231   return(multigram_add_words_to_grammar(lm, multigram_get_grammar_by_name(lm, gramname), winfo));
01232 }
01233 
01259 boolean
01260 multigram_add_words_to_grammar_by_id(PROCESS_LM *lm, unsigned short id, WORD_INFO *winfo)
01261 {
01262   return(multigram_add_words_to_grammar(lm, multigram_get_grammar_by_id(lm, id), winfo));
01263 }
01264 
01265 /* end of file */