/** * @file hmm_check.c * * * @brief トライフォンの辞書上での整合性チェック * * * * @brief Triphone checker on word dictionary * * * @author Akinobu LEE * @date Thu Mar 17 20:50:07 2005 * * $Revision: 1.4 $ * */ /* * Copyright (c) 1991-2012 Kawahara Lab., Kyoto University * Copyright (c) 2000-2005 Shikano Lab., Nara Institute of Science and Technology * Copyright (c) 2005-2012 Julius project team, Nagoya Institute of Technology * All rights reserved */ #include #define PHSTEP 10 ///< Malloc step for phoneme conversion /** * * @brief 音素列からHMM列への変換を行ない,結果を表示する. * * このルーチンは,Julius/Julian に与えられた音響モデルと * HMMList ファイルにおいて,音素列からHMM列への変換をテストする * ための関数である. * * 空白で区切られた音素列の文字列に対して,トライフォンモデル使用時には * コンテキストが考慮され,最終的に対応する HMM 列へ変換される. * その後,変換した結果を, * - 音素列から導かれる本来の適用すべきモデル名 * - 上記を HMMList にしたがって変換した論理 HMM 名 * - 実際に計算で用いられる物理HMM名または pseudo HMM 名 * の順に出力する. * * なお,文字列中に "|" を含めることで,そこを単語区切りとして扱い, * トライフォンにおいて単語間の展開を考慮することができる. * * @param str [i/o] 空白で区切られた音素列の文字列 * @param hmminfo [in] HMM定義構造体 * @param len_ret [out] 返り値の論理 HMM の要素数 * * @return 新たにメモリ割り付けられた変換後の論理HMMのポインタ列 * * * @brief Convert phoneme sequences to logical HMM sequences, and output the * result. * * This function is for testing procedure to convert words in dictionary * to corresponding HMM sequences in Julius/Julian, given an HMMList and * HTK HMM definition. * * Given a space-separated list of phoneme names in a string, each phonemes * will be converted to context-dependent style (if using triphone model), * and then converted to HMM sequence that will finally be used for * recognition. Then, the following data will be output for all HMM: * - Original phone HMM name, * - Logical HMM name that is converted from above, * - Physical or pseudo HMM name that will actually be used. * * Additionally, specifying '|' in the string gives a word boundary between * phonemes, and triphone conversion will consider the cross-word expansion. * * @param str [i/o] string that contains space-saparated phoneme sequence. * @param hmminfo [in] HMM definition structure * @param len_ret [out] num of elements in the return value * * @return the newly allocated pointer array to the converted logical HMMs. * */ static HMM_Logical ** new_str2phseq(char *str, HTK_HMM_INFO *hmminfo, int *len_ret) { char **tokens; boolean *word_end; int phnum; boolean word_mode = FALSE; HMM_Logical **new; static char buf[MAX_HMMNAME_LEN]; /* read in string and divide into token unit */ { char *p; int tokenmax; tokenmax = PHSTEP; tokens = (char **)mymalloc(sizeof(char *) * tokenmax); word_end = (boolean *)mymalloc(sizeof(boolean) * tokenmax); phnum = 0; for(p = strtok(str, DELM); p; p = strtok(NULL, DELM)) { if (strmatch(p, "|")) { word_mode = TRUE; if (phnum > 0) word_end[phnum-1] = TRUE; continue; } if (phnum >= tokenmax) { tokenmax += PHSTEP; tokens = (char **)myrealloc(tokens, sizeof(char *) * tokenmax); word_end = (boolean *)myrealloc(word_end, sizeof(boolean) * tokenmax); } tokens[phnum] = strcpy((char *)mymalloc(strlen(p)+1), p); word_end[phnum] = FALSE; phnum++; } if (phnum == 0) { jlog("ERROR: hmm_check: no phone specified\n"); printf("ERROR: hmm_check: no phone specified\n"); new = NULL; goto spend; } word_end[phnum-1] = TRUE; } /* check if the phonemes exist in basephone list */ { BASEPHONE *ph; int i; boolean ok_flag = TRUE; for (i=0;ibasephone.root); if (ph == NULL || ! strmatch(ph->name, tokens[i])) { jlog("ERROR: hmm_check: %2d - unknown phone \"%s\"\n", i+1, tokens[i]); printf("ERROR: hmm_check: %2d - unknown phone \"%s\"\n", i+1, tokens[i]); ok_flag = FALSE; continue; } } if (! ok_flag) { jlog("ERROR: hmm_check: unknown phone(s)\n"); printf("ERROR: hmm_check: unknown phone(s)\n"); new = NULL; goto spend; } } /* token -> original logical name -> logical HMM -> physical/pseudo phone */ /* cross-word conversion and fallback to bi/mono-phone is also considered */ { int i; char *hmmstr; HMM_Logical *lg; boolean ok_flag = TRUE; new = (HMM_Logical **)mymalloc(sizeof(HMM_Logical *) * phnum); /* original logical name, applied logical HMM name (defined by HMMList), and the actual physical/pseudo HMM name (defined in hmmdefs) */ printf("\n id original logical physical/pseudo\n"); printf(" -------------------------------------------------\n"); if (hmminfo->is_triphone) { cycle_triphone(NULL); cycle_triphone(tokens[0]); for (i = 0; i < phnum; i++) { if (i < phnum - 1) { hmmstr = cycle_triphone(tokens[i+1]); } else { hmmstr = cycle_triphone_flush(); } lg = htk_hmmdata_lookup_logical(hmminfo, hmmstr); if (lg == NULL) { if (word_mode) { if (i > 0 && word_end[i-1]) { if (word_end[i]) { center_name(hmmstr, buf); } else { rightcenter_name(hmmstr, buf); } } else if (word_end[i]) { leftcenter_name(hmmstr, buf); } lg = htk_hmmdata_lookup_logical(hmminfo, buf); if (lg == NULL) { jlog("ERROR: hmm_check: no defined/pseudo HMM for \"%s\"??\n", buf); printf("ERROR: hmm_check: no defined/pseudo HMM for \"%s\"??\n", buf); ok_flag = FALSE; continue; } if (lg->is_pseudo) { printf(" %2d: %11s -> (pseudo) -> {%s}\n", i+1, hmmstr, lg->body.pseudo->name); } else { printf(" %2d: %11s -> %8s -> [%s]\n", i+1, hmmstr, lg->name, lg->body.defined->name); } } else { jlog("ERROR: hmm_check: UNKNOWN %2d: (%s)\n", i+1, hmmstr); printf("ERROR: hmm_check: UNKNOWN %2d: (%s)\n", i+1, hmmstr); ok_flag = FALSE; continue; } } else { if (lg->is_pseudo) { printf(" %2d: %11s -> (pseudo) -> {%s}\n", i+1, hmmstr, lg->body.pseudo->name); } else { printf(" %2d: %11s -> %8s -> [%s]\n", i+1, hmmstr, " ", lg->body.defined->name); } } new[i] = lg; } } else { for (i = 0; i < phnum; i++) { lg = htk_hmmdata_lookup_logical(hmminfo, tokens[i]); if (lg == NULL) { jlog("ERROR: hmm_check: %2d - unknown logical HMM \"%s\"\n", i+1, tokens[i]); printf("ERROR: hmm_check: %2d - unknown logical HMM \"%s\"\n", i+1, tokens[i]); ok_flag = FALSE; continue; } new[i] = lg; } } if (ok_flag) { printf("succeeded\n"); } else { jlog("ERROR: hmm_check: failed\n"); printf("failed\n"); free(new); new = NULL; goto spend; } } spend: { int i; for(i=0;i * 標準入力から1行を音素列表記として読み込み,トライフォンへの変換チェックを * 行なう. * * @param hmminfo [in] HMM定義構造体 * * * Read in line from stdin as phoneme sequence and try convertion to * triphone for checking. * * @param hmminfo [in] HMM definition structure * */ static boolean test_expand_triphone(HTK_HMM_INFO *hmminfo) { char *buf; int newline; HMM_Logical **phseq; int phlen; boolean flag = FALSE; buf = (char *)mymalloc(4096); for(;;) { /* read in phoneme sequence from stdin */ printf(">>> input phone sequence (word delimiter is `|', blank to return)\n"); if (fgets(buf, 4096, stdin) == NULL) { flag = TRUE; break; } newline = strlen(buf)-1; /* chop newline */ if (buf[newline] == '\n') buf[newline] = '\0'; if (buf[0] == '\0') break; /* convert string to phseq and output */ phseq = new_str2phseq(buf, hmminfo, &phlen); free(phseq); } free(buf); return flag; } /** * * コマンドライン上でトライフォンのチェックを行なうモード ("-check triphone"). * * @param r [in] 認識処理インスタンス * * * Mode to do interactive triphone conversion check ("-check triphone"). * * @param r [in] recognition process instance * * * @callgraph * @callergraph */ void hmm_check(RecogProcess *r) { boolean endflag; static char cmd[MAX_HMMNAME_LEN]; int newline; printf("*************************************************\n"); printf("******** TRIPHONE COHERENCE CHECK MODE ********\n"); printf("*************************************************\n"); printf("hmmdefs=%s\n", r->am->config->hmmfilename); if (r->am->config->mapfilename != NULL) { printf("hmmlist=%s\n", r->am->config->mapfilename); } printf("dict=%s\n", r->lm->config->dictfilename); printf("headsil = "); put_voca(stdout, r->lm->winfo, r->lm->winfo->head_silwid); printf("tailsil = "); put_voca(stdout, r->lm->winfo, r->lm->winfo->tail_silwid); if (make_base_phone(r->am->hmminfo, r->lm->winfo) == FALSE) { jlog("ERROR: hmm_check: error in making base phone list\n"); printf("ERROR: hmm_check: error in making base phone list\n"); return; } print_phone_info(stdout, r->am->hmminfo); for(endflag = FALSE; endflag == FALSE;) { printf("===== command (\"H\" for help) > "); if (fgets(cmd, MAX_HMMNAME_LEN, stdin) == NULL) break; newline = strlen(cmd)-1; /* chop newline */ if (cmd[newline] == '\n') cmd[newline] = '\0'; if (cmd[0] == '\0') continue; /* if blank line, read next */ switch(cmd[0]) { case 'a': /* all */ /* check if logical HMMs cover all possible variants */ test_interword_triphone(r->am->hmminfo, r->lm->winfo); break; case 'c': /* conv */ /* try to expand triphone for given phoneme sequence */ endflag = test_expand_triphone(r->am->hmminfo); break; case 'i': /* info */ /* output data source */ printf("hmmdefs=%s\n", r->am->config->hmmfilename); if (r->am->config->mapfilename != NULL) { printf("hmmlist=%s\n", r->am->config->mapfilename); } printf("dict=%s\n", r->lm->config->dictfilename); printf("headsil = "); put_voca(stdout, r->lm->winfo, r->lm->winfo->head_silwid); printf("tailsil = "); put_voca(stdout, r->lm->winfo, r->lm->winfo->tail_silwid); print_phone_info(stdout, r->am->hmminfo); break; case 'p': /* phonelist */ /* output basephone */ print_all_basephone_name(&(r->am->hmminfo->basephone)); break; case 'd': /* phonelist in detail */ /* output basephone */ print_all_basephone_detail(&(r->am->hmminfo->basephone)); break; case 'q': /* quit */ /* quit this check mode */ endflag = TRUE; break; default: printf("COMMANDS:\n"); printf(" info --- output HMM information\n"); printf(" conv --- try HMM conversion for given phone sequence\n"); printf(" phonelist --- print base phone list\n"); printf(" all --- check if all possible IW-triphone is covered\n"); printf(" quit --- quit\n"); break; } } printf("*************************************************\n"); printf("***** END OF TRIPHONE COHERENCE CHECK MODE ****\n"); printf("*************************************************\n"); } /* end of file */