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

/* rdhmmdef_tiedmix.c --- sub-routine for tied-mixture model */

/* $Id: rdhmmdef_tiedmix.c,v 1.3 2002/09/11 22:01:50 ri Exp $ */

#include <sent/stddefs.h>
#include <sent/htk_hmm.h>

extern char *rdhmmdef_token;	/* defined in rdhmmdef.c */

/* lookup codebook by name */
static GCODEBOOK *
codebook_lookup(HTK_HMM_INFO *hmm, char *keyname)
{
  GCODEBOOK *book;

  if (hmm->codebook_root == NULL) return(NULL);
  book = aptree_search_data(keyname, hmm->codebook_root);
  if (strmatch(book->name, keyname)) {
    return book;
  } else {
    return NULL;
  }
}

/* add new codebook to lookup index */
static void
codebook_add(HTK_HMM_INFO *hmm, GCODEBOOK *new)
{
  GCODEBOOK *match;
  if (hmm->codebook_root == NULL) {
    hmm->codebook_root = aptree_make_root_node(new);
  } else {
    match = aptree_search_data(new->name, hmm->codebook_root);
    if (strmatch(match->name, new->name)) {
      j_printerr("Error: ~s \"%s\" is already defined\n", new->name);
      rderr(NULL);
    } else {
      aptree_add_entry(new->name, new, match->name, &(hmm->codebook_root));
    }
  }
}

/* make index for GCODEBOOK id -> HTK_HMM_Dens * */
/* error exit if ~m macro "<name><id>" not found */
static void
tmix_create_codebook_index(HTK_HMM_INFO *hmminfo, GCODEBOOK *book)
{
  char *mixname;
  HTK_HMM_Dens *dtmp;
  int i;
  int realbooknum = 0;

  mixname = (char *)mymalloc(strlen(book->name)+30);
  book->d = (HTK_HMM_Dens **) mybmalloc(sizeof(HTK_HMM_Dens *) * book->num);
  for (i=0;i<book->num;i++) {
    sprintf(mixname, "%s%d", book->name, i + 1);
    if ((dtmp = dens_lookup(hmminfo, mixname)) == NULL) {
/* 
 *	 j_printerr("Error: mixture \"%s\" (%dth mixture in codebook \"%s\") not found\n", mixname, i + 1, book->name);
 *	 rderr(NULL);
 */
      book->d[i] = NULL;
    } else {
      book->d[i] = dtmp;
      realbooknum++;
    }
  }
  if (realbooknum < book->num) {
    j_printerr("Warning: book [%s]: defined=%d < %d\n",
	       book->name, realbooknum, book->num);
  }
  
  free(mixname);
}

/* read in TMIX array */
/* GCODEBOOK structure will be built from mixture macros when referred from <TMix> directive */
/* mixture macros that is never referred will be treated as normal mixture */
void
tmix_read(FILE *fp, HTK_HMM_State *state, HTK_HMM_INFO *hmm)
{
  char *bookname;
  GCODEBOOK *thebook;
  int mid, i;

  NoTokErr("missing TMIX bookname");
  bookname = rdhmmdef_token;
  /* check whether the specified codebook exist */
  if ((thebook = codebook_lookup(hmm, bookname)) == NULL) {
    /* create GCODEBOOK global index structure from mixture macros */
    thebook = (GCODEBOOK *)mybmalloc(sizeof(GCODEBOOK));
    thebook->name = mybstrdup(bookname);
    thebook->num = state->mix_num;
    /* map codebook id to HTK_HMM_Dens* */
    tmix_create_codebook_index(hmm, thebook);
    /* register the new codebook */
    codebook_add(hmm, thebook);
    thebook->id = hmm->codebooknum;
    hmm->codebooknum++;
    /* set maximum codebook size */
    if (hmm->maxcodebooksize < thebook->num) hmm->maxcodebooksize = thebook->num;
  } else {
    /* check coherence */
    if (state->mix_num != thebook->num) {
      rderr("tmix_read: TMIX weight num don't match the codebook size");
    }
  }

  /* set pointer to the GCODEBOOK structure  */
  state->b = (HTK_HMM_Dens **)thebook;

  /* store the weights to `state->bweight[]' */
  read_token(fp);
  state->bweight = (PROB *) mybmalloc(sizeof(PROB) * state->mix_num);
  {
    int len;
    double w;

    mid = 0;
    while (mid < state->mix_num)
    {
      char *p, q;
      NoTokErr("missing some TMIX weights");
      if ((p = strchr(rdhmmdef_token, '*')) == NULL) {
	len = 1;
	w = atof(rdhmmdef_token);
      } else {
	len = atoi(p+1);
	q = *p;
	*p = '\0';
	w = atof(rdhmmdef_token);
	*p = q;
      }
      read_token(fp);
      for(i=0;i<len;i++) {
	state->bweight[mid] = log(w);
	mid++;
      }
    }
  }

  /* mark info as tied mixture */
  hmm->is_tied_mixture = TRUE;
}

