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