1 /**
2  * @file   hmm_check.c
3  *
4  * <JA>
5  * @brief  �ȥ饤�ե���μ����Ǥ������������å�
6  * </JA>
7  *
8  * <EN>
9  * @brief  Triphone checker on word dictionary
10  * </EN>
11  *
12  * @author Akinobu LEE
13  * @date   Thu Mar 17 20:50:07 2005
14  *
15  * $Revision: 1.2 $
16  *
17  */
18 /*
19  * Copyright (c) 1991-2007 Kawahara Lab., Kyoto University
20  * Copyright (c) 2000-2005 Shikano Lab., Nara Institute of Science and Technology
21  * Copyright (c) 2005-2007 Julius project team, Nagoya Institute of Technology
22  * All rights reserved
23  */
24 
25 #include <julius/julius.h>
26 
27 #define PHSTEP 10		///< Malloc step for phoneme conversion
28 
29 /**
30  * <JA>
31  * @brief  ��������HMM��ؤ��Ѵ���Ԥʤ�����̤�ɽ������.
32  *
33  * ���Υ롼����ϡ�Julius/Julian ��Ϳ����줿������ǥ��
34  * HMMList �ե�����ˤ����ơ���������HMM��ؤ��Ѵ���ƥ��Ȥ���
35  * ����δؿ��Ǥ���.
36  *
37  * ����Ƕ��ڤ�줿�������ʸ������Ф��ơ��ȥ饤�ե����ǥ���ѻ��ˤ�
38  * ����ƥ����Ȥ���θ���졤�ǽ�Ū���б����� HMM ����Ѵ������.
39  * ���θ塤�Ѵ�������̤�
40  *   - ��������Ƴ����������Ŭ�Ѥ��٤���ǥ�̾
41  *   - �嵭�� HMMList �ˤ������ä��Ѵ��������� HMM ̾
42  *   - �ºݤ˷׻����Ѥ�����ʪ��HMM̾�ޤ��� pseudo HMM ̾
43  * �ν�˽��Ϥ���.
44  *
45  * �ʤ���ʸ������� "|" ��ޤ�뤳�Ȥǡ�������ñ����ڤ�Ȥ��ư�����
46  * �ȥ饤�ե���ˤ�����ñ��֤�Ÿ�����θ���뤳�Ȥ��Ǥ���.
47  *
48  * @param str [i/o] ����Ƕ��ڤ�줿�������ʸ����
49  * @param hmminfo [in] HMM�����¤��
50  * @param len_ret [out] �֤��ͤ����� HMM �����ǿ�
51  *
52  * @return �����˥������դ���줿�Ѵ��������HMM�Υݥ�����
53  * </JA>
54  * <EN>
55  * @brief  Convert phoneme sequences to logical HMM sequences, and output the
56  * result.
57  *
58  * This function is for testing procedure to convert words in dictionary
59  * to corresponding HMM sequences in Julius/Julian, given an HMMList and
60  * HTK HMM definition.
61  *
62  * Given a space-separated list of phoneme names in a string, each phonemes
63  * will be converted to context-dependent style (if using triphone model),
64  * and then converted to HMM sequence that will finally be used for
65  * recognition.  Then, the following data will be output for all HMM:
66  *   - Original phone HMM name,
67  *   - Logical HMM name that is converted from above,
68  *   - Physical or pseudo HMM name that will actually be used.
69  *
70  * Additionally, specifying '|' in the string gives a word boundary between
71  * phonemes, and triphone conversion will consider the cross-word expansion.
72  *
73  * @param str [i/o] string that contains space-saparated phoneme sequence.
74  * @param hmminfo [in] HMM definition structure
75  * @param len_ret [out] num of elements in the return value
76  *
77  * @return the newly allocated pointer array to the converted logical HMMs.
78  * </EN>
79  */
80 static HMM_Logical **
new_str2phseq(char * str,HTK_HMM_INFO * hmminfo,int * len_ret)81 new_str2phseq(char *str, HTK_HMM_INFO *hmminfo, int *len_ret)
82 {
83   char **tokens;
84   boolean *word_end;
85   int phnum;
86   boolean word_mode = FALSE;
87   HMM_Logical **new;
88   static char buf[MAX_HMMNAME_LEN];
89 
90   /* read in string and divide into token unit */
91   {
92     char *p;
93     int tokenmax;
94     tokenmax = PHSTEP;
95     tokens = (char **)mymalloc(sizeof(char *) * tokenmax);
96     word_end = (boolean *)mymalloc(sizeof(boolean) * tokenmax);
97     phnum = 0;
98     for(p = strtok(str, DELM); p; p = strtok(NULL, DELM)) {
99       if (strmatch(p, "|")) {
100 	word_mode = TRUE;
101 	if (phnum > 0) word_end[phnum-1] = TRUE;
102 	continue;
103       }
104       if (phnum >= tokenmax) {
105 	tokenmax += PHSTEP;
106 	tokens = (char **)myrealloc(tokens, sizeof(char *) * tokenmax);
107 	word_end = (boolean *)myrealloc(word_end, sizeof(boolean) * tokenmax);
108       }
109       tokens[phnum] = strcpy((char *)mymalloc(strlen(p)+1), p);
110       word_end[phnum] = FALSE;
111       phnum++;
112     }
113     if (phnum == 0) {
114       jlog("ERROR: hmm_check: no phone specified\n");
115       printf("ERROR: hmm_check: no phone specified\n");
116       new = NULL;
117       goto spend;
118     }
119     word_end[phnum-1] = TRUE;
120   }
121   /* check if the phonemes exist in basephone list */
122   {
123     BASEPHONE *ph;
124     int i;
125     boolean ok_flag = TRUE;
126     for (i=0;i<phnum;i++) {
127       ph = aptree_search_data(tokens[i], hmminfo->basephone.root);
128       if (ph == NULL || ! strmatch(ph->name, tokens[i])) {
129 	jlog("ERROR: hmm_check: %2d - unknown phone \"%s\"\n", i+1, tokens[i]);
130 	printf("ERROR: hmm_check: %2d - unknown phone \"%s\"\n", i+1, tokens[i]);
131 	ok_flag = FALSE;
132 	continue;
133       }
134     }
135     if (! ok_flag) {
136       jlog("ERROR: hmm_check: unknown phone(s)\n");
137       printf("ERROR: hmm_check: unknown phone(s)\n");
138       new = NULL;
139       goto spend;
140     }
141   }
142   /* token -> original logical name -> logical HMM -> physical/pseudo phone */
143   /* cross-word conversion and fallback to bi/mono-phone is also considered */
144   {
145     int i;
146     char *hmmstr;
147     HMM_Logical *lg;
148     boolean ok_flag = TRUE;
149 
150     new = (HMM_Logical **)mymalloc(sizeof(HMM_Logical *) * phnum);
151 
152     /* original logical name, applied logical HMM name (defined by HMMList),
153        and the actual physical/pseudo HMM name (defined in hmmdefs) */
154     printf("\n  id     original   logical    physical/pseudo\n");
155     printf(" -------------------------------------------------\n");
156 
157 
158     if (hmminfo->is_triphone) {
159       cycle_triphone(NULL);
160       cycle_triphone(tokens[0]);
161       for (i = 0; i < phnum; i++) {
162 	if (i < phnum - 1) {
163 	  hmmstr = cycle_triphone(tokens[i+1]);
164 	} else {
165 	  hmmstr = cycle_triphone_flush();
166 	}
167 	lg = htk_hmmdata_lookup_logical(hmminfo, hmmstr);
168 	if (lg == NULL) {
169 	  if (word_mode) {
170 	    if (i > 0 && word_end[i-1]) {
171 	      if (word_end[i]) {
172 		center_name(hmmstr, buf);
173 	      } else {
174 		rightcenter_name(hmmstr, buf);
175 	      }
176 	    } else if (word_end[i]) {
177 	      leftcenter_name(hmmstr, buf);
178 	    }
179 	    lg = htk_hmmdata_lookup_logical(hmminfo, buf);
180 	    if (lg == NULL) {
181 	      jlog("ERROR: hmm_check: no defined/pseudo HMM for \"%s\"??\n", buf);
182 	      printf("ERROR: hmm_check: no defined/pseudo HMM for \"%s\"??\n", buf);
183 	      ok_flag = FALSE;
184 	      continue;
185 	    }
186 	    if (lg->is_pseudo) {
187 	      printf("  %2d: %11s -> (pseudo) -> {%s}\n", i+1, hmmstr, lg->body.pseudo->name);
188 	    } else {
189 	      printf("  %2d: %11s -> %8s -> [%s]\n", i+1, hmmstr, lg->name, lg->body.defined->name);
190 	    }
191 	  } else {
192 	    jlog("ERROR: hmm_check: UNKNOWN %2d: (%s)\n", i+1, hmmstr);
193 	    printf("ERROR: hmm_check: UNKNOWN %2d: (%s)\n", i+1, hmmstr);
194 	    ok_flag = FALSE;
195 	    continue;
196 	  }
197 	} else {
198 	  if (lg->is_pseudo) {
199 	    printf("  %2d: %11s -> (pseudo) -> {%s}\n", i+1, hmmstr, lg->body.pseudo->name);
200 	  } else {
201 	    printf("  %2d: %11s -> %8s -> [%s]\n", i+1, hmmstr, " ", lg->body.defined->name);
202 	  }
203 	}
204 	new[i] = lg;
205       }
206     } else {
207       for (i = 0; i < phnum; i++) {
208 	lg = htk_hmmdata_lookup_logical(hmminfo, tokens[i]);
209 	if (lg == NULL) {
210 	  jlog("ERROR: hmm_check: %2d - unknown logical HMM \"%s\"\n", i+1, tokens[i]);
211 	  printf("ERROR: hmm_check: %2d - unknown logical HMM \"%s\"\n", i+1, tokens[i]);
212 	  ok_flag = FALSE;
213 	  continue;
214 	}
215 	new[i] = lg;
216       }
217     }
218     if (ok_flag) {
219       printf("succeeded\n");
220     } else {
221       jlog("ERROR: hmm_check: failed\n");
222       printf("failed\n");
223       free(new);
224       new = NULL;
225       goto spend;
226     }
227 
228   }
229 
230  spend:
231   {
232     int i;
233     for(i=0;i<phnum;i++) {
234       free(tokens[i]);
235     }
236     free(tokens);
237     free(word_end);
238   }
239 
240   *len_ret = phnum;
241 
242   return new;
243 }
244 
245 /**
246  * <JA>
247  * ɸ�����Ϥ���1�Ԥ�����ɽ���Ȥ����ɤ߹��ߡ��ȥ饤�ե���ؤ��Ѵ������å���
248  * �Ԥʤ�.
249  *
250  * @param hmminfo [in] HMM�����¤��
251  * </JA>
252  * <EN>
253  * Read in line from stdin as phoneme sequence and try convertion to
254  * triphone for checking.
255  *
256  * @param hmminfo [in] HMM definition structure
257  * </EN>
258  */
259 static boolean
test_expand_triphone(HTK_HMM_INFO * hmminfo)260 test_expand_triphone(HTK_HMM_INFO *hmminfo)
261 {
262   char *buf;
263   int newline;
264   HMM_Logical **phseq;
265   int phlen;
266   boolean flag = FALSE;
267 
268   buf = (char *)mymalloc(4096);
269   for(;;) {
270     /* read in phoneme sequence from stdin */
271     printf(">>> input phone sequence (word delimiter is `|', blank to return)\n");
272     if (fgets(buf, 4096, stdin) == NULL) {
273       flag = TRUE;
274       break;
275     }
276     newline = strlen(buf)-1;    /* chop newline */
277     if (buf[newline] == '\n') buf[newline] = '\0';
278     if (buf[0] == '\0') break;
279     /* convert string to phseq and output */
280     phseq = new_str2phseq(buf, hmminfo, &phlen);
281     free(phseq);
282   }
283   free(buf);
284   return flag;
285 }
286 
287 /**
288  * <JA>
289  * ���ޥ�ɥ饤���ǥȥ饤�ե���Υ����å���Ԥʤ��⡼�� ("-check triphone").
290  *
291  * @param r [in] ǧ��������������
292  * </JA>
293  * <EN>
294  * Mode to do interactive triphone conversion check ("-check triphone").
295  *
296  * @param r [in] recognition process instance
297  * </EN>
298  *
299  * @callgraph
300  * @callergraph
301  */
302 void
hmm_check(RecogProcess * r)303 hmm_check(RecogProcess *r)
304 {
305   boolean endflag;
306   static char cmd[MAX_HMMNAME_LEN];
307   int newline;
308 
309   printf("*************************************************\n");
310   printf("********  TRIPHONE COHERENCE CHECK MODE  ********\n");
311   printf("*************************************************\n");
312 
313   printf("hmmdefs=%s\n", r->am->config->hmmfilename);
314   if (r->am->config->mapfilename != NULL) {
315     printf("hmmlist=%s\n", r->am->config->mapfilename);
316   }
317   printf("dict=%s\n", r->lm->config->dictfilename);
318   printf("headsil = "); put_voca(stdout, r->lm->winfo, r->lm->winfo->head_silwid);
319   printf("tailsil = "); put_voca(stdout, r->lm->winfo, r->lm->winfo->tail_silwid);
320 
321   if (make_base_phone(r->am->hmminfo, r->lm->winfo) == FALSE) {
322     jlog("ERROR: hmm_check: error in making base phone list\n");
323     printf("ERROR: hmm_check: error in making base phone list\n");
324     return;
325   }
326 
327   print_phone_info(stdout, r->am->hmminfo);
328 
329   for(endflag = FALSE; endflag == FALSE;) {
330     printf("===== command (\"H\" for help) > ");
331     if (fgets(cmd, MAX_HMMNAME_LEN, stdin) == NULL) break;
332     newline = strlen(cmd)-1;    /* chop newline */
333     if (cmd[newline] == '\n') cmd[newline] = '\0';
334     if (cmd[0] == '\0') continue; /* if blank line, read next */
335 
336     switch(cmd[0]) {
337     case 'a':			/* all */
338       /* check if logical HMMs cover all possible variants */
339       test_interword_triphone(r->am->hmminfo, r->lm->winfo);
340       break;
341     case 'c':			/* conv */
342       /* try to expand triphone for given phoneme sequence */
343       endflag = test_expand_triphone(r->am->hmminfo);
344       break;
345     case 'i':			/* info */
346       /* output data source */
347       printf("hmmdefs=%s\n", r->am->config->hmmfilename);
348       if (r->am->config->mapfilename != NULL) {
349 	printf("hmmlist=%s\n", r->am->config->mapfilename);
350       }
351       printf("dict=%s\n", r->lm->config->dictfilename);
352       printf("headsil = "); put_voca(stdout, r->lm->winfo, r->lm->winfo->head_silwid);
353       printf("tailsil = "); put_voca(stdout, r->lm->winfo, r->lm->winfo->tail_silwid);
354       print_phone_info(stdout, r->am->hmminfo);
355       break;
356     case 'p':			/* phonelist */
357       /* output basephone */
358       print_all_basephone_name(&(r->am->hmminfo->basephone));
359       break;
360     case 'd':			/* phonelist in detail */
361       /* output basephone */
362       print_all_basephone_detail(&(r->am->hmminfo->basephone));
363       break;
364     case 'q':			/* quit */
365       /* quit this check mode */
366       endflag = TRUE;
367       break;
368     default:
369       printf("COMMANDS:\n");
370       printf(" info      --- output HMM information\n");
371       printf(" conv      --- try HMM conversion for given phone sequence\n");
372       printf(" phonelist --- print base phone list\n");
373       printf(" all       --- check if all possible IW-triphone is covered\n");
374       printf(" quit      --- quit\n");
375       break;
376     }
377   }
378   printf("*************************************************\n");
379   printf("*****  END OF TRIPHONE COHERENCE CHECK MODE  ****\n");
380   printf("*************************************************\n");
381 }
382 /* end of file */
383