/** 
 * Read next token and ste it to rdhmmdef_token.
 * 
 * @param fp [in] file pointer
 * 
 * @return the pointer to the read token, or NULL on end of file or error.
 */
char *
read_token(FILE *fp)
{
  int len;
  int bp = 0;
  int maxlen = MAXBUFLEN;
  static char delims[] = HMMDEF_DELM;

  if ((rdhmmdef_token = mystrtok_quote(NULL, HMMDEF_DELM)) != NULL) {
    /* has token */
    if (mystrtok_movetonext(NULL, HMMDEF_DELM) != NULL || last_line_full == FALSE) {
      /* return the current token, if this is not a last token, or
	 last is newline terminated */
      return rdhmmdef_token;
    } else {
      /* concatinate the last token with next line */
      len = strlen(rdhmmdef_token);
      memmove(buf, rdhmmdef_token, len);
      bp = len;
      maxlen -= len;
    }
  }

  /* read new 1 line a*/
  while(
#ifdef HAVE_ZLIB
	gzgets((gzFile)fp, &(buf[bp]), maxlen) != Z_NULL
#else
	fgets(&(buf[bp]), maxlen, fp) != NULL
#endif
	) {
    /* chop delimiters at end of line (incl. newline) */
    /* if no delimiter at end of line, last_line_full is TRUE */
    last_line_full = TRUE;
    len = strlen(buf)-1;
    while (len >= 0 && strchr(delims, buf[len])) {
      last_line_full = FALSE;
      buf[len--] = '\0';
    }
    if (buf[0] != '\0') {
      /* start getting next token */
      rdhmmdef_token = mystrtok_quote(buf, HMMDEF_DELM);
      /* increment line */
      line++;
      return rdhmmdef_token;
    }
  }
  /* when reading error, return NULL */
  rdhmmdef_token = NULL;
  return rdhmmdef_token;
}
/**
 * Sub function to Add a dictionary entry line to the word dictionary.
 *
 * @param buf [i/o] buffer to hold the input string, will be modified in this function
 * @param vnum_p [in] current number of words in @a winfo
 * @param linenum [in] current line number of the input
 * @param winfo [out] pointer to word dictionary to append the data.
 * @param hmminfo [in] HTK %HMM definition data.  if NULL, phonemes are ignored.
 * @param do_conv [in] TRUE if performing triphone conversion
 * @param ok_flag [out] will be set to FALSE if an error occured for this input.
 *
 * @return FALSE if buf == "DICEND", else TRUE will be returned.
 */
boolean
voca_load_htkdict_line(char *buf, WORD_ID *vnum_p, int linenum, WORD_INFO *winfo, HTK_HMM_INFO *hmminfo, boolean do_conv, boolean *ok_flag)
{
    char *ptmp, *lp = NULL, *p;
    static char cbuf[MAX_HMMNAME_LEN];
    HMM_Logical **tmpwseq;
    int len;
    HMM_Logical *tmplg;
    boolean pok;
    int vnum;

    vnum = *vnum_p;

    if (strmatch(buf, "DICEND")) return FALSE;

    /* allocate temporal work area for the first call */
    if (winfo->work == NULL) {
        winfo->work_num = PHONEMELEN_STEP;
        winfo->work = (void *)mybmalloc2(sizeof(HMM_Logical *) * winfo->work_num, &(winfo->mroot));
    }
    tmpwseq = (HMM_Logical **)winfo->work;

    /* backup whole line for debug output */
    strcpy(bufbak, buf);

    /* GrammarEntry */
    if ((ptmp = mystrtok_quote(buf, " \t\n")) == NULL) {
        jlog("Error: voca_load_htkdict: line %d: corrupted data:\n> %s\n", linenum, bufbak);
        winfo->errnum++;
        *ok_flag = FALSE;
        return TRUE;
    }
    winfo->wname[vnum] = strcpy((char *)mybmalloc2(strlen(ptmp)+1, &(winfo->mroot)), ptmp);

    /* just move pointer to next token */
    if ((ptmp = mystrtok_movetonext(NULL, " \t\n")) == NULL) {
        jlog("Error: voca_load_htkdict: line %d: corrupted data:\n> %s\n", linenum, bufbak);
        winfo->errnum++;
        *ok_flag = FALSE;
        return TRUE;
    }
#ifdef CLASS_NGRAM
    winfo->cprob[vnum] = 0.0;	/* prob = 1.0, logprob = 0.0 */
#endif

    if (ptmp[0] == '@') {		/* class N-gram prob */
#ifdef CLASS_NGRAM
        /* word probability within the class (for class N-gram) */
        /* format: classname @classprob wordname [output] phoneseq */
        /* classname equals to wname, and wordname will be omitted */
        /* format: @%f (log scale) */
        /* if "@" not found or "@0", it means class == word */
        if ((ptmp = mystrtok(NULL, " \t\n")) == NULL) {
            jlog("Error: voca_load_htkdict: line %d: corrupted data:\n> %s\n", linenum, bufbak);
            winfo->errnum++;
            *ok_flag = FALSE;
            return TRUE;
        }
        if (ptmp[1] == '\0') {	/* space between '@' and figures */
            jlog("Error: voca_load_htkdict: line %d: value after '@' missing, maybe wrong space?\n> %s\n", linenum, bufbak);
            winfo->errnum++;
            *ok_flag = FALSE;
            return TRUE;
        }
        winfo->cprob[vnum] = atof(&(ptmp[1]));
        if (winfo->cprob[vnum] != 0.0) winfo->cwnum++;
        /* read next word entry (just skip them) */
        if ((ptmp = mystrtok(NULL, " \t\n")) == NULL) {
            jlog("Error: voca_load_htkdict: line %d: corrupted data:\n> %s\n", linenum,bufbak);
            winfo->errnum++;
            *ok_flag = FALSE;
            return TRUE;
        }
        /* move to the next word entry */
        if ((ptmp = mystrtok_movetonext(NULL, " \t\n")) == NULL) {
            jlog("Error: voca_load_htkdict: line %d: corrupted data:\n> %s\n", linenum, bufbak);
            winfo->errnum++;
            *ok_flag = FALSE;
            return TRUE;
        }
#else  /* ~CLASS_NGRAM */
        jlog("Error: voca_load_htkdict: line %d: cannot handle in-class word probability\n> %s\n", linenum, ptmp, bufbak);
        winfo->errnum++;
        *ok_flag = FALSE;
        return TRUE;
#endif /* CLASS_NGRAM */
    }

    /* OutputString */
    switch(ptmp[0]) {
    case '[':			/* not transparent word */
        winfo->is_transparent[vnum] = FALSE;
        ptmp = mystrtok_quotation(NULL, " \t\n", '[', ']', 0);
        break;
    case '{':			/* transparent word */
        winfo->is_transparent[vnum] = TRUE;
        ptmp = mystrtok_quotation(NULL, " \t\n", '{', '}', 0);
        break;
    default:
#if 1
        /* ALLOW no entry for output */
        /* same as wname is used */
        winfo->is_transparent[vnum] = FALSE;
        ptmp = winfo->wname[vnum];
#else
        /* error */
        jlog("Error: voca_load_htkdict: line %d: missing output string??\n> %s\n", linenum, bufbak);
        winfo->errnum++;
        *ok_flag = FALSE;
        return TRUE;
#endif
    }
    if (ptmp == NULL) {
        jlog("Error: voca_load_htkdict: line %d: corrupted data:\n> %s\n", linenum, bufbak);
        winfo->errnum++;
        *ok_flag = FALSE;
        return TRUE;
    }
    winfo->woutput[vnum] = strcpy((char *)mybmalloc2(strlen(ptmp)+1, &(winfo->mroot)), ptmp);

    /* phoneme sequence */
    if (hmminfo == NULL) {
        /* don't read */
        winfo->wseq[vnum] = NULL;
        winfo->wlen[vnum] = 0;
    } else {

        /* store converted phone sequence to temporal bufffer */
        len = 0;

        if (do_conv) {
            /* convert phoneme to triphone expression (word-internal) */
            cycle_triphone(NULL);
            if ((lp = mystrtok(NULL, " \t\n")) == NULL) {
                jlog("Error: voca_load_htkdict: line %d: word %s has no phoneme:\n> %s\n", linenum, winfo->wname[vnum], bufbak);
                winfo->errnum++;
                *ok_flag = FALSE;
                return TRUE;
            }
            if (strlen(lp) >= MAX_HMMNAME_LEN) {
                jlog("Error: voca_load_htkdict: line %d: too long phone name: %s\n", linenum, lp);
                winfo->errnum++;
                *ok_flag = FALSE;
                return TRUE;
            }
            cycle_triphone(lp);
        }

        pok = TRUE;
        for (;;) {
            if (do_conv) {
                /*	if (lp != NULL) jlog(" %d%s",len,lp);*/
                if (lp != NULL) lp = mystrtok(NULL, " \t\n");
                if (lp != NULL) {
                    if (strlen(lp) >= MAX_HMMNAME_LEN) {
                        jlog("Error: voca_load_htkdict: line %d: too long phone name: %s\n", linenum, lp);
                        winfo->errnum++;
                        *ok_flag = FALSE;
                        return TRUE;
                    }
                    p = cycle_triphone(lp);
                }
                else p = cycle_triphone_flush();
            } else {
                p = mystrtok(NULL, " \t\n");
            }
            if (p == NULL) break;

            /* both defined/pseudo phone is allowed */
            tmplg = htk_hmmdata_lookup_logical(hmminfo, p);
            if (tmplg == NULL) {
                /* not found */
                if (do_conv) {
                    /* both defined or pseudo phone are not found */
                    if (len == 0 && lp == NULL) {
                        jlog("Error: voca_load_htkdict: line %d: triphone \"*-%s+*\" or monophone \"%s\" not found\n", linenum, p, p);
                        snprintf(cbuf,MAX_HMMNAME_LEN,"*-%s+* or monophone %s", p, p);
                    } else if (len == 0) {
                        jlog("Error: voca_load_htkdict: line %d: triphone \"*-%s\" or biphone \"%s\" not found\n", linenum, p, p);
                        snprintf(cbuf,MAX_HMMNAME_LEN,"*-%s or biphone %s", p, p);
                    } else if (lp == NULL) {
                        jlog("Error: voca_load_htkdict: line %d: triphone \"%s+*\" or biphone \"%s\" not found\n", linenum, p, p);
                        snprintf(cbuf,MAX_HMMNAME_LEN,"%s+* or biphone %s", p, p);
                    } else {
                        jlog("Error: voca_load_htkdict: line %d: triphone \"%s\" not found\n", linenum, p);
                        snprintf(cbuf,MAX_HMMNAME_LEN,"%s", p);
                    }
                } else {
                    jlog("Error: voca_load_htkdict: line %d: phone \"%s\" not found\n", linenum, p);
                    snprintf(cbuf, MAX_HMMNAME_LEN, "%s", p);
                }
                add_to_error(winfo, cbuf);
                pok = FALSE;
            } else {
                /* found */
                if (len >= winfo->work_num) {
                    /* expand wseq area by PHONEMELEN_STEP */
                    winfo->work_num += PHONEMELEN_STEP;
                    winfo->work = (void *)mybmalloc2(sizeof(HMM_Logical *) * winfo->work_num, &(winfo->mroot));
                    memcpy(winfo->work, tmpwseq, sizeof(HMM_Logical *) * (winfo->work_num - PHONEMELEN_STEP));
                    tmpwseq = (HMM_Logical **)winfo->work;
                }
                /* store to temporal buffer */
                tmpwseq[len] = tmplg;
            }
            len++;
        }
        if (!pok) {			/* error in phoneme */
            jlog("Error: voca_load_htkdict: the line content was: %s\n", bufbak);
            winfo->errnum++;
            *ok_flag = FALSE;
            return TRUE;
        }
        if (len == 0) {
            jlog("Error: voca_load_htkdict: line %d: no phone specified:\n> %s\n", linenum, bufbak);
            winfo->errnum++;
            *ok_flag = FALSE;
            return TRUE;
        }
        /* store to winfo */
        winfo->wseq[vnum] = (HMM_Logical **)mybmalloc2(sizeof(HMM_Logical *) * len, &(winfo->mroot));
        memcpy(winfo->wseq[vnum], tmpwseq, sizeof(HMM_Logical *) * len);
        winfo->wlen[vnum] = len;
    }

    vnum++;

    *vnum_p = vnum;

    return(TRUE);
}
Exemple #3
0
/** 
 * Load CMN parameter from file.  If the number of MFCC dimension in the
 * file does not match the specified one, an error will occur.
 *
 * Format can be either HTK ascii format or binary format (made by Julius older than ver.4.2.3)
 * 
 * @param c [i/o] CMN calculation work area
 * @param filename [in] file name
 * 
 * @return TRUE on success, FALSE on failure.
 */
boolean
CMN_load_from_file(CMNWork *c, char *filename)
{
  FILE *fp;
  int veclen;
  char ch[5];
  char *buf;

  jlog("Stat: wav2mfcc-pipe: reading initial cepstral mean/variance from file \"%s\"\n", filename);
  if ((fp = fopen_readfile(filename)) == NULL) {
    jlog("Error: wav2mfcc-pipe: failed to open %s\n", filename);
    return(FALSE);
  }

  /* detect file format */
  if (myread(&ch, sizeof(char), 5, fp) == FALSE) {
    jlog("Error: wav2mfcc-pipe: failed to read CMN/CVN file\n");
    fclose_readfile(fp);
    return(FALSE);
  }

  myfrewind(fp);
  if (ch[0] == '<' &&
      (ch[1] == 'C' || ch[1] == 'c') &&
      (ch[2] == 'E' || ch[2] == 'e') &&
      (ch[3] == 'P' || ch[3] == 'p') &&
      (ch[4] == 'S' || ch[4] == 's') ) {
    /* ascii HTK format (>=4.3) */
    char *p;
    int mode;
    int d, dv, len;

    jlog("Stat: wav2mfcc-pipe: reading HTK-format cepstral vectors\n");
    buf = (char *)mymalloc(MAXBUFLEN);
    mode = 0;
    while(getl(buf, MAXBUFLEN, fp) != NULL) {
      for (p = mystrtok_quote(buf, "<> \t\r\n"); p; p = mystrtok_quote(NULL, "<> \t\r\n")) {
	switch(mode){
	case 0:
	  if (strmatch(p, "MEAN")) {
	    mode = 1;
	  } else if (strmatch(p, "VARIANCE")) {
	    mode = 3;
	  }
	  break;
	case 1:
	  len = atof(p);
	  if (len != c->veclen && len != c->mfcc_dim) {
	    jlog("Error: wav2mfcc-pipe: cepstral dimension mismatch\n");
	    jlog("Error: wav2mfcc-pipe: process = %d (%d), file = %d\n", c->veclen, c->mfcc_dim, len);
	    free(buf); fclose_readfile(fp);
	    return(FALSE);
	  }
	  for (d = 0; d < c->veclen; d++) c->cmean_init[d] = 0.0;
	  d = 0;
	  mode = 2;
	  break;
	case 2:
	  if (strmatch(p, "VARIANCE")) {
	    mode = 3;
	  } else {
	    if (d >= len) {
	      jlog("Error: wav2mfcc-pipe: corrupted data\n");
	      free(buf); fclose_readfile(fp);
	      return(FALSE);
	    }
	    c->cmean_init[d++] = atof(p);
	  }
	  break;
	case 3:
	  len = atof(p);
	  if (len != c->veclen) {
	    jlog("Error: wav2mfcc-pipe: cepstral dimension mismatch\n");
	    jlog("Error: wav2mfcc-pipe: process = %d, file = %d\n", c->veclen, len);
	    free(buf); fclose_readfile(fp);
	    return(FALSE);
	  }
	  dv = 0;
	  mode = 4;
	  break;
	case 4:
	  if (dv >= len) {
	    jlog("Error: wav2mfcc-pipe: corrupted data\n");
	    free(buf); fclose_readfile(fp);
	    return(FALSE);
	  }
	  c->cvar_init[dv++] = atof(p);
	  break;
	}
      }
    }
    free(buf);
    if (d != len || (mode >= 3 && dv != len)) {
      jlog("Error: wav2mfcc-pipe: corrupted data\n");
      fclose_readfile(fp);
      return(FALSE);
    }
  } else {
    /* binary (<4.3) */
    jlog("Stat: wav2mfcc-pipe: reading binary-format cepstral vectors\n");
    /* read header */
    if (myread(&veclen, sizeof(int), 1, fp) == FALSE) {
      jlog("Error: wav2mfcc-pipe: failed to read header\n");
      fclose_readfile(fp);
      return(FALSE);
    }
    /* check length */
    if (veclen != c->veclen) {
      jlog("Error: wav2mfcc-pipe: cepstral dimension mismatch\n");
      jlog("Error: wav2mfcc-pipe: process = %d, file = %d\n", c->veclen, veclen);
      fclose_readfile(fp);
      return(FALSE);
    }
    /* read body */
    if (myread(c->cmean_init, sizeof(float), c->veclen, fp) == FALSE) {
      jlog("Error: wav2mfcc-pipe: failed to read mean for CMN\n");
      fclose_readfile(fp);
      return(FALSE);
    }
    if (c->var) {
      if (myread(c->cvar_init, sizeof(float), c->veclen, fp) == FALSE) {
	jlog("Error: wav2mfcc-pipe: failed to read variance for CVN\n");
	fclose_readfile(fp);
	return(FALSE);
      }
    }
  }

  if (fclose_readfile(fp) == -1) {
    jlog("Error: wav2mfcc-pipe: failed to close\n");
    return(FALSE);
  }

  c->cmean_init_set = TRUE;
  c->loaded_from_file = TRUE;
  jlog("Stat: wav2mfcc-pipe: finished reading CMN/CVN parameter\n");

  return(TRUE);
}
/**
 * Sub function to Add a dictionary entry line to the word dictionary.
 *
 * @param buf [i/o] buffer to hold the input string, will be modified in this function
 * @param vnum_p [in] current number of words in @a winfo
 * @param linenum [in] current line number of the input
 * @param winfo [out] pointer to word dictionary to append the data.
 * @param hmminfo [in] HTK %HMM definition data.  if NULL, phonemes are ignored.
 * @param do_conv [in] TRUE if performing triphone conversion
 * @param ok_flag [out] will be set to FALSE if an error occured for this input.
 * @param headphone [in] word head silence model name
 * @param tailphone [in] word tail silence model name
 * @param contextphone [in] silence context name to be used at head and tail
 *
 * @return FALSE if buf == "DICEND", else TRUE will be returned.
 */
boolean
voca_load_wordlist_line(char *buf, WORD_ID *vnum_p, int linenum, WORD_INFO *winfo, HTK_HMM_INFO *hmminfo, boolean do_conv, boolean *ok_flag, char *headphone, char *tailphone, char *contextphone)
{
    char *ptmp, *lp = NULL, *p;
    static char cbuf[MAX_HMMNAME_LEN];
    static HMM_Logical **tmpwseq = NULL;
    static int tmpmaxlen;
    int len;
    HMM_Logical *tmplg;
    boolean pok, first;
    int vnum;

    vnum = *vnum_p;

    if (strmatch(buf, "DICEND")) return FALSE;

    /* allocate temporal work area for the first call */
    if (tmpwseq == NULL) {
        tmpmaxlen = PHONEMELEN_STEP;
        tmpwseq = (HMM_Logical **)mymalloc(sizeof(HMM_Logical *) * tmpmaxlen);
    }

    /* backup whole line for debug output */
    strcpy(bufbak, buf);

    /* Output string */
    if ((ptmp = mystrtok_quote(buf, " \t\n")) == NULL) {
        jlog("Error: voca_load_wordlist: line %d: corrupted data:\n> %s\n", linenum, bufbak);
        winfo->errnum++;
        *ok_flag = FALSE;
        return TRUE;
    }
    winfo->wname[vnum] = strcpy((char *)mybmalloc2(strlen(ptmp)+1, &(winfo->mroot)), ptmp);

    /* reset transparent flag */
    winfo->is_transparent[vnum] = FALSE;

    /* just move pointer to next token */
    if ((ptmp = mystrtok_movetonext(NULL, " \t\n")) == NULL) {
        jlog("Error: voca_load_wordlist: line %d: corrupted data:\n> %s\n", linenum, bufbak);
        winfo->errnum++;
        *ok_flag = FALSE;
        return TRUE;
    }
#ifdef CLASS_NGRAM
    winfo->cprob[vnum] = 0.0;	/* prob = 1.0, logprob = 0.0 */
#endif

    if (ptmp[0] == '@') {		/* class N-gram prob */
#ifdef CLASS_NGRAM
        /* word probability within the class (for class N-gram) */
        /* format: classname @classprob wordname [output] phoneseq */
        /* classname equals to wname, and wordname will be omitted */
        /* format: @%f (log scale) */
        /* if "@" not found or "@0", it means class == word */
        if ((ptmp = mystrtok(NULL, " \t\n")) == NULL) {
            jlog("Error: voca_load_wordlist: line %d: corrupted data:\n> %s\n", linenum, bufbak);
            winfo->errnum++;
            *ok_flag = FALSE;
            return TRUE;
        }
        if (ptmp[1] == '\0') {	/* space between '@' and figures */
            jlog("Error: voca_load_wordlist: line %d: value after '@' missing, maybe wrong space?\n> %s\n", linenum, bufbak);
            winfo->errnum++;
            *ok_flag = FALSE;
            return TRUE;
        }
        winfo->cprob[vnum] = atof(&(ptmp[1]));
        if (winfo->cprob[vnum] != 0.0) winfo->cwnum++;
        /* read next word entry (just skip them) */
        if ((ptmp = mystrtok(NULL, " \t\n")) == NULL) {
            jlog("Error: voca_load_wordlist: line %d: corrupted data:\n> %s\n", linenum,bufbak);
            winfo->errnum++;
            *ok_flag = FALSE;
            return TRUE;
        }
        /* move to the next word entry */
        if ((ptmp = mystrtok_movetonext(NULL, " \t\n")) == NULL) {
            jlog("Error: voca_load_wordlist: line %d: corrupted data:\n> %s\n", linenum, bufbak);
            winfo->errnum++;
            *ok_flag = FALSE;
            return TRUE;
        }
#else  /* ~CLASS_NGRAM */
        jlog("Error: voca_load_wordlist: line %d: cannot handle in-class word probability\n> %s\n", linenum, ptmp, bufbak);
        winfo->errnum++;
        *ok_flag = FALSE;
        return TRUE;
#endif /* CLASS_NGRAM */
    }

    /* OutputString */
    switch(ptmp[0]) {
    case '[':			/* ignore transparency */
        ptmp = mystrtok_quotation(NULL, " \t\n", '[', ']', 0);
        break;
    case '{':			/* ignore transparency */
        ptmp = mystrtok_quotation(NULL, " \t\n", '{', '}', 0);
        break;
    default:
        /* ALLOW no entry for output */
        /* same as wname is used */
        ptmp = winfo->wname[vnum];
    }
    if (ptmp == NULL) {
        jlog("Error: voca_load_htkdict: line %d: corrupted data:\n> %s\n", linenum, bufbak);
        winfo->errnum++;
        *ok_flag = FALSE;
        return TRUE;
    }
    winfo->woutput[vnum] = strcpy((char *)mybmalloc2(strlen(ptmp)+1, &(winfo->mroot)), ptmp);

    /* phoneme sequence */
    if (hmminfo == NULL) {
        /* don't read */
        winfo->wseq[vnum] = NULL;
        winfo->wlen[vnum] = 0;
    } else {

        len = 0;
        first = TRUE;
        pok = TRUE;

        for (;;) {
            if (do_conv) {
                if (first) {
                    /* init phone cycler */
                    cycle_triphone(NULL);
                    /* insert head phone at beginning of word */
                    if (contextphone) {
                        if (strlen(contextphone) >= MAX_HMMNAME_LEN) {
                            jlog("Error: voca_load_htkdict: line %d: too long phone name: %s\n", linenum, contextphone);
                            winfo->errnum++;
                            *ok_flag = FALSE;
                            return TRUE;
                        }
                        cycle_triphone(contextphone);
                    } else {
                        cycle_triphone("NULL_C");
                    }
                    if ((lp = mystrtok(NULL, " \t\n")) == NULL) {
                        jlog("Error: voca_load_wordlist: line %d: word %s has no phoneme:\n> %s\n", linenum, winfo->wname[vnum], bufbak);
                        winfo->errnum++;
                        *ok_flag = FALSE;
                        return TRUE;
                    }
                    if (strlen(lp) >= MAX_HMMNAME_LEN) {
                        jlog("Error: voca_load_htkdict: line %d: too long phone name: %s\n", linenum, lp);
                        winfo->errnum++;
                        *ok_flag = FALSE;
                        return TRUE;
                    }
                    p = cycle_triphone(lp);
                    first = FALSE;
                } else {		/* do_conv, not first */
                    if (lp != NULL) {	/* some token processed at last loop */
                        lp = mystrtok(NULL, " \t\n");
                        if (lp != NULL) {
                            /* token exist */
                            if (strlen(lp) >= MAX_HMMNAME_LEN) {
                                jlog("Error: voca_load_htkdict: line %d: too long phone name: %s\n", linenum, lp);
                                winfo->errnum++;
                                *ok_flag = FALSE;
                                return TRUE;
                            }
                            p = cycle_triphone(lp);
                        } else {
                            /* no more token, insert tail phone at end of word */
                            if (contextphone) {
                                if (strlen(contextphone) >= MAX_HMMNAME_LEN) {
                                    jlog("Error: voca_load_htkdict: line %d: too long phone name: %s\n", linenum, contextphone);
                                    winfo->errnum++;
                                    *ok_flag = FALSE;
                                    return TRUE;
                                }
                                p = cycle_triphone(contextphone);
                            } else {
                                p = cycle_triphone("NULL_C");
                            }
                        }
                    } else {		/* no more token at last input  */
                        /* flush tone cycler */
                        p = cycle_triphone_flush();
                    }
                }
            } else {			/* not do_conv */
                if (first) {
                    p = lp = headphone;
                    first = FALSE;
                } else {
                    if (lp != NULL) {	/* some token processed at last loop */
                        p = lp = mystrtok(NULL, " \t\n");
                        /* if no more token, use tailphone */
                        if (lp == NULL) p = tailphone;
                    } else {
                        /* no more token at last input, exit loop */
                        p = NULL;
                    }
                }
            }
            if (p == NULL) break;
            /* for headphone and tailphone, their context should not be handled */
            /* and when they appear as context they should be replaced by contextphone */
            if (do_conv) {
                center_name(p, cbuf);
                if (contextphone) {
                    if (strmatch(cbuf, contextphone)) {
                        if (len == 0) {
                            p = headphone;
                        } else if (lp == NULL) {
                            p = tailphone;
                        }
                    }
                } else {
                    if (strmatch(cbuf, "NULL_C")) {
                        if (len == 0) {
                            p = headphone;
                        } else if (lp == NULL) {
                            p = tailphone;
                        }
                    } else {
                        if (strnmatch(p, "NULL_C", 6)) {
                            if (strnmatch(&(p[strlen(p)-6]), "NULL_C", 6)) {
                                p = cbuf;
                            } else {
                                p = rightcenter_name(p, cbuf);
                            }
                        } else if (strnmatch(&(p[strlen(p)-6]), "NULL_C", 6)) {
                            p = leftcenter_name(p, cbuf);
                        }
                    }
                }
            }
            //printf("[[%s]]\n", p);

            /* both defined/pseudo phone is allowed */
            tmplg = htk_hmmdata_lookup_logical(hmminfo, p);
            if (tmplg == NULL) {
                /* not found */
                if (do_conv) {
                    /* logical phone was not found */
                    jlog("Error: voca_load_wordlist: line %d: logical phone \"%s\" not found\n", linenum, p);
                    snprintf(cbuf,MAX_HMMNAME_LEN,"%s", p);
                } else {
                    jlog("Error: voca_load_wordlist: line %d: phone \"%s\" not found\n", linenum, p);
                    snprintf(cbuf, MAX_HMMNAME_LEN, "%s", p);
                }
                add_to_error(winfo, cbuf);
                pok = FALSE;
            } else {
                /* found */
                if (len >= tmpmaxlen) {
                    /* expand wseq area by PHONEMELEN_STEP */
                    tmpmaxlen += PHONEMELEN_STEP;
                    tmpwseq = (HMM_Logical **)myrealloc(tmpwseq, sizeof(HMM_Logical *) * tmpmaxlen);
                }
                /* store to temporal buffer */
                tmpwseq[len] = tmplg;
            }
            len++;
        }
        if (!pok) {			/* error in phoneme */
            jlog("Error: voca_load_wordlist: the line content was: %s\n", bufbak);
            winfo->errnum++;
            *ok_flag = FALSE;
            return TRUE;
        }
        if (len == 0) {
            jlog("Error: voca_load_wordlist: line %d: no phone specified:\n> %s\n", linenum, bufbak);
            winfo->errnum++;
            *ok_flag = FALSE;
            return TRUE;
        }
        /* store to winfo */
        winfo->wseq[vnum] = (HMM_Logical **)mybmalloc2(sizeof(HMM_Logical *) * len, &(winfo->mroot));
        memcpy(winfo->wseq[vnum], tmpwseq, sizeof(HMM_Logical *) * len);
        winfo->wlen[vnum] = len;
        winfo->wton[vnum] = 0;
    }

    vnum++;
    *vnum_p = vnum;

    return(TRUE);
}
/** 
 * @brief  Main top routine to read in HTK %HMM definition file.
 *
 * A HTK %HMM definition file will be read from @a fp.  After reading,
 * the parameter type is checked and calculate some statistics.
 * 
 * @param fp [in] file pointer
 * @param hmm [out] pointer to a %HMM definition structure to store data.
 * 
 * @return TRUE on success, FALSE on failure.
 */
boolean
rdhmmdef(FILE *fp, HTK_HMM_INFO *hmm)
{
  char macrosw;
  char *name;

  /* variances in htkdefs are not inversed yet */
  hmm->variance_inversed = FALSE;

  /* read the first token */
  /* read new 1 line */
  line = 1;
  if (getl(buf, MAXBUFLEN, fp) == NULL) {
    rdhmmdef_token = NULL;
  } else {
    rdhmmdef_token = mystrtok_quote(buf, HMMDEF_DELM);
  }
  
  /* the toplevel loop */
  while (rdhmmdef_token != NULL) {/* break on EOF */
    if (rdhmmdef_token[0] != '~') { /* toplevel commands are always macro */
      return FALSE;
    }
    macrosw = rdhmmdef_token[1];
    read_token(fp);		/* read next token after the "~.."  */
    switch(macrosw) {
    case 'o':			/* global option */
      if (set_global_opt(fp,hmm) == FALSE) {
	return FALSE;
      }
      break;
    case 't':			/* transition macro */
      name = mybstrdup2(rdhmmdef_token, &(hmm->mroot));
      if (strlen(name) >= MAX_HMMNAME_LEN) rderr("Macro name too long");
      read_token(fp);
      def_trans_macro(name, fp, hmm);
      break;
    case 's':			/* state macro */
      name = mybstrdup2(rdhmmdef_token, &(hmm->mroot));
      if (strlen(name) >= MAX_HMMNAME_LEN) rderr("Macro name too long");
      read_token(fp);
      def_state_macro(name, fp, hmm);
      break;
    case 'm':			/* density (mixture) macro */
      name = mybstrdup2(rdhmmdef_token, &(hmm->mroot));
      if (strlen(name) >= MAX_HMMNAME_LEN) rderr("Macro name too long");
      read_token(fp);
      def_dens_macro(name, fp, hmm);
      break;
    case 'h':			/* HMM define */
      name = mybstrdup2(rdhmmdef_token, &(hmm->mroot));
      if (strlen(name) >= MAX_HMMNAME_LEN) rderr("Macro name too long");
      read_token(fp);
      def_HMM(name, fp, hmm);
      break;
    case 'v':			/* Variance macro */
      name = mybstrdup2(rdhmmdef_token, &(hmm->mroot));
      if (strlen(name) >= MAX_HMMNAME_LEN) rderr("Macro name too long");
      read_token(fp);
      def_var_macro(name, fp, hmm);
      break;
    case 'w':			/* Stream weight macro */
      name = mybstrdup2(rdhmmdef_token, &(hmm->mroot));
      if (strlen(name) >= MAX_HMMNAME_LEN) rderr("Macro name too long");
      read_token(fp);
      def_streamweight_macro(name, fp, hmm);
      break;
    case 'r':			/* Regression class macro (ignore) */
      name = mybstrdup2(rdhmmdef_token, &(hmm->mroot));
      if (strlen(name) >= MAX_HMMNAME_LEN) rderr("Macro name too long");
      read_token(fp);
      def_regtree_macro(name, fp, hmm);
      break;
    case 'p':			/* Mixture pdf macro (extension of HTS) */
      name = mybstrdup2(rdhmmdef_token, &(hmm->mroot));
      if (strlen(name) >= MAX_HMMNAME_LEN) rderr("Macro name too long");
      read_token(fp);
      def_mpdf_macro(name, fp, hmm);
      break;
    }
  }

  /* convert transition prob to log scale */
  conv_log_arc(hmm);

  jlog("Stat: rdhmmdef: ascii format HMM definition\n");
  
  /* check limitation */
  if (check_all_hmm_limit(hmm)) {
    jlog("Stat: rdhmmdef: limit check passed\n");
  } else {
    jlog("Error: rdhmmdef: cannot handle this HMM due to system limitation\n");
    return FALSE;
  }

  /* determine whether this model needs multi-path handling */
  hmm->need_multipath = htk_hmm_has_several_arc_on_edge(hmm);
  if (hmm->need_multipath) {
    jlog("Stat: rdhmmdef: this HMM requires multipath handling at decoding\n");
  } else {
    jlog("Stat: rdhmmdef: this HMM does not need multipath handling\n");
  }
  
  /* inverse all variance values for faster computation */
  if (! hmm->variance_inversed) {
    htk_hmm_inverse_variances(hmm);
    hmm->variance_inversed = TRUE;
  }

  /* check HMM parameter option type */
  if (!check_hmm_options(hmm)) {
    jlog("Error: rdhmmdef: hmm options check failed\n");
    return FALSE;
  }

  /* add ID number for all HTK_HMM_State if not assigned */
  {
    HTK_HMM_State *stmp;
    int n;
    boolean has_sid;

    /* caclculate total num and check if has sid */
    has_sid = FALSE;
    n = 0;
    for (stmp = hmm->ststart; stmp; stmp = stmp->next) {
      n++;
      if (n >= MAX_STATE_NUM) {
	jlog("Error: rdhmmdef: too much states in a model > %d\n", MAX_STATE_NUM);
	return FALSE;
      }
      if (stmp->id != -1) {
	has_sid = TRUE;
      }
    }
    hmm->totalstatenum = n;
    if (has_sid) {
      jlog("Stat: rdhmmdef: <SID> found in the definition\n");
      /* check if each state is assigned a valid sid */
      if (htk_hmm_check_sid(hmm) == FALSE) {
	jlog("Error: rdhmmdef: error in SID\n");
	return FALSE;
      }
    } else {
      /* assign internal sid (will not be saved) */
      jlog("Stat: rdhmmdef: no <SID> embedded\n");
      jlog("Stat: rdhmmdef: assign SID by the order of appearance\n");
      n = hmm->totalstatenum;
      for (stmp = hmm->ststart; stmp; stmp = stmp->next) {
	stmp->id = --n;
      }
    }
  }
  /* calculate the maximum number of mixture */
  {
    HTK_HMM_State *stmp;
    int max, s, mix;
    max = 0;
    for (stmp = hmm->ststart; stmp; stmp = stmp->next) {
      for(s=0;s<stmp->nstream;s++) {
	mix = stmp->pdf[s]->mix_num;
	if (max < mix) max = mix;
      }
    }
    hmm->maxmixturenum = max;
  }
  /* compute total number of HMM models and maximum length */
  {
    HTK_HMM_Data *dtmp;
    int n, maxlen;
    n = 0;
    maxlen = 0;
    for (dtmp = hmm->start; dtmp; dtmp = dtmp->next) {
      if (maxlen < dtmp->state_num) maxlen = dtmp->state_num;
      n++;
    }
    hmm->maxstatenum = maxlen;
    hmm->totalhmmnum = n;
  }
  /* compute total number of Gaussians */
  {
    HTK_HMM_Dens *dtmp;
    int n = 0;
    for (dtmp = hmm->dnstart; dtmp; dtmp = dtmp->next) {
      n++;
    }
    hmm->totalmixnum = n;
  }
  /* check of HMM name length exceed the maximum */
  {
    HTK_HMM_Dens *dtmp;
    int n = 0;
    for (dtmp = hmm->dnstart; dtmp; dtmp = dtmp->next) {
      n++;
    }
    hmm->totalmixnum = n;
  }
  /* compute total number of mixture PDFs */
  {
    HTK_HMM_PDF *p;
    int n = 0;
    for (p = hmm->pdfstart; p; p = p->next) {
      n++;
    }
    hmm->totalpdfnum = n;
  }
  /* assign ID number for all HTK_HMM_Trans */
  {
    HTK_HMM_Trans *ttmp;
    int n = 0;
    for (ttmp = hmm->trstart; ttmp; ttmp = ttmp->next) {
      ttmp->id = n++;
    }
    hmm->totaltransnum = n;
  }
#ifdef ENABLE_MSD
  /* check if MSD-HMM */
  htk_hmm_check_msd(hmm);
#endif

  return(TRUE);			/* success */
}