/* Copyright (c) 1991-2002 Doshita Lab. Speech Group, Kyoto University */
/* Copyright (c) 2000-2002 Speech and Acoustics Processing Lab., NAIST */
/*   All rights reserved   */

/* hmm_check.c --- debug routine for triphone model check */

/* $Id: hmm_check.c,v 1.5 2002/09/11 22:02:33 ri Exp $ */

#include <julius.h>


#define PHSTEP 10

/* 音素名→HMM名変換チェッカー

   音素列の文字列(空白区切り)を triphone HMM 列に変換する．
   単語区切り('|')を入れた場合は単語内/単語間を考慮する．
   これで実際にどのような triphone が適用されるのかをチェックできる．

   本来のtriphone表記→logical HMM→physical/pseudo HMM のマッピングを
   標準出力に出力する． */
/* phoneme -> HMM conversion checker: convert phoneme string (each separated
   by space, word boundary by '|') into HMM sequence.  Inter-word/cross-word
   triphone conversion is considered, and the logical phone's mapping to
   pseudo/physical HMM is output in stdout. */
static HMM_Logical **
new_str2phseq(char *str, int *len_return, HTK_HMM_INFO *hmminfo)
{
  char **tokens;
  boolean *word_end;
  int phnum;
  boolean word_mode = FALSE;
  HMM_Logical **new;
  
  /* 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 = first_token(str); p; p = next_token_if_any()) {
      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) {
      j_printf("failed: 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;i<phnum;i++) {
      ph = aptree_search_data(tokens[i], hmminfo->basephone.root);
      if (! strmatch(ph->name, tokens[i])) {
	j_printf("         %2d: unknown phone \"%s\"\n", i+1, tokens[i]);
	ok_flag = FALSE;
	continue;
      }
    }
    if (! ok_flag) {
      j_printf("failed\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;
    char buf[50];
    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) */
    j_printf("\n  id     original   logical    physical/pseudo\n");
    j_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) {
	      j_printf("InternalError: no defined/pseudo HMM for \"%s\"??\n", buf);
	      ok_flag = FALSE;
	      continue;
	    }
	    if (lg->is_pseudo) {
	      j_printf("  %2d: %11s -> (pseudo) -> {%s}\n", i+1, hmmstr, lg->body.pseudo->name);
	    } else {
	      j_printf("  %2d: %11s -> %8s -> [%s]\n", i+1, hmmstr, lg->name, lg->body.defined->name);
	    }
	  } else {
	    j_printf(" UNKNOWN %2d: (%s)\n", i+1, hmmstr);
	    ok_flag = FALSE;
	    continue;
	  }
	} else {
	  if (lg->is_pseudo) {
	    j_printf("  %2d: %11s -> (pseudo) -> {%s}\n", i+1, hmmstr, lg->body.pseudo->name);
	  } else {
	    j_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) {
	  j_printf("%2d: unknown logical HMM \"%s\"\n", i+1, tokens[i]);
	  ok_flag = FALSE;
	  continue;
	}
	new[i] = lg;
      }
    }
    if (ok_flag) {
      j_printf("succeeded\n");
    } else {
      j_printf("failed\n");
      free(new);
      new = NULL;
      goto spend;
    }
      
  }

 spend:
  {
    int i;
    for(i=0;i<phnum;i++) {
      free(tokens[i]);
    }
    free(tokens);
    free(word_end);
  }

  return new;
}

/* read in 1 line as phoneme sequence and try to convert it to triphone */
static void
test_expand_triphone(HTK_HMM_INFO *hmminfo)
{
  char *buf;
  int newline;
  HMM *whmm;
  HMM_Logical **phseq;
  int phlen;

  for(;;) {
    /* read in phoneme sequence from stdin */
    j_printf(">>> input phone sequence (word delimiter is `|', blank to return)\n");
    buf = (char *)mymalloc(4096);
    if (fgets(buf, 4096, stdin) == NULL) 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, &phlen, hmminfo);
    if (phseq == NULL) continue;
    /* make HMM from phseq */
    /*whmm = new_make_word_hmm(hmminfo, phseq, phlen);*/
  }
  free(buf);
}

/* interactive triphone conversion check for given hmmdefs/hmmlist */
void
hmm_check(HTK_HMM_INFO *hmminfo, WORD_INFO *winfo)
{
  boolean endflag;
  char cmd[50];
  int newline;
  
  j_printf("\n\n");
  j_printf("*************************************************\n");
  j_printf("********  TRIPHONE COHERENCE CHECK MODE  ********\n");
  j_printf("*************************************************\n");
  j_printf("\n");

  j_printf("hmmdefs=%s\n", hmmfilename);
  if (mapfilename != NULL) {
    j_printf("hmmlist=%s\n", mapfilename);
  }
  j_printf("dict=%s\n", dictfilename);
  j_printf("headsil = "); put_voca(winfo, winfo->head_silwid);
  j_printf("tailsil = "); put_voca(winfo, winfo->tail_silwid);

  make_base_phone(hmminfo, winfo);
  print_phone_info(hmminfo);

  for(endflag = FALSE; endflag == FALSE;) {
    j_printf("===== command (\"H\" for help) > ");
    if (fgets(cmd, 50, stdin) == NULL) continue;
    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(hmminfo, winfo);
      break;
    case 'c':			/* conv */
      /* try to expand triphone for given phoneme sequence */
      test_expand_triphone(hmminfo);
      break;
    case 'i':			/* info */
      /* output data source */
      j_printf("hmmdefs=%s\n", hmmfilename);
      if (mapfilename != NULL) {
	j_printf("hmmlist=%s\n", mapfilename);
      }
      j_printf("dict=%s\n", dictfilename);
      j_printf("headsil = "); put_voca(winfo, winfo->head_silwid);
      j_printf("tailsil = "); put_voca(winfo, winfo->tail_silwid);
      print_phone_info(hmminfo);
      break;
    case 'p':			/* phonelist */
      /* output basephone */
      print_all_basephone_name(&(hmminfo->basephone));
      break;
    case 'd':			/* phonelist in detail */
      /* output basephone */
      print_all_basephone_detail(&(hmminfo->basephone));
      break;
    case 'H':
      j_printf("COMMANDS:\n");
      j_printf("info      --- output HMM information\n");
      j_printf("conv      --- try HMM conversion for given phone sequence\n");
      j_printf("phonelist --- print base phone list\n");
      j_printf("all       --- check if all possible IW-triphone is covered\n");
      j_printf("quit      --- quit\n");
      break;
    case 'q':			/* quit */
      /* quit this check mode */
      endflag = TRUE;
      break;
    }
  }
  j_printf("\n");
  j_printf("*************************************************\n");
  j_printf("*****  END OF TRIPHONE COHERENCE CHECK MODE  ****\n");
  j_printf("*************************************************\n");
  j_printf("\n");
}
