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

/* realtime-1stpass.c --- on-the-fly 1st pass decoding with realtime CMN */

/* $Id: realtime-1stpass.c,v 1.9 2002/11/06 16:56:41 ri Exp $ */

/* do speech input, make mfcc (attach), viterbi in single loop */

/* the main RealTimePipeLine will be called for each input segment,
   as a callback from adin_cut() */

#include <julius.h>

#undef RDEBUG

/* MFCC parameter */
static HTK_Param *param = NULL;	/* hold MFCC param */
static Value para;		/* parameters for Wav2MFCC */
static float *bf;		/* work space for FFT */

static boolean need_attach = FALSE; /* whether param need selection */
static int maxframelen;		/* maximum frame length from MAXSPEECHLEN */

static boolean last_is_segmented; /* TRUE if last pass was a segmented input */
static int last_time;		/* end frame */
#ifdef SP_BREAK_CURRENT_FRAME
static SP16 *rest_Speech = NULL; /* speech sample not processed in previous segment */
static int rest_alloc_len = 0;
static int rest_len;
#endif

/**** progress pointers ****/
/*
 * [SPEECH DATA]
 *    0               nowlen
 *    |---------------|
 * ooo.....
 *   o........
 *      ........
 *        ........
 *
 *
 * [PARAMETER VECTOR]
 *     O o o o o o f_raw
 *     O o o o o o
 *     O . . . . .                        (MFCC+RAW_E 13)
 *     O o o o o o                        
 *     O o o o |-|para.delWin
 *     O o o o                            (MFCC+RAW_E+delta MFCC + delta E 26)
 *     O o o o
 *       o o o
 *     |---------|f_delay = para.delWin * 2 + 1
 *     
 *     f ... (MFCC + delta MFCC + delta E 25) + realtime-CMN
 */   

static int f_raw;
static int f_delay;
static int f;
static SP16 *window;
static int windowlen;
static int windownum;		/* current left samples in a window */

/* configure MFCC parameter in para */
static void
init_para()
{
  para.smp_period = smpPeriod;
  para.framesize  = fsize;
  para.frameshift = fshift;
  para.preEmph    = DEF_PREENPH;
  para.mfcc_dim   = DEF_MFCCDIM;
  para.lifter     = DEF_CEPLIF;
  para.delWin     = delwin;
  para.silFloor   = DEF_SILFLOOR;
  para.hipass     = hipass;
  para.lopass     = lopass;
  para.c0         = c0_required;
  /* para.escale     = DEF_ESCALE; */
  para.escale     = 1.0;
  para.fbank_num  = DEF_FBANK;
  para.ss_alpha   = ssalpha;
  para.ss_floor   = ssfloor;
  /* para.cmn        = FALSE;*/
  para.cmn        = TRUE;
  para.enormal    = FALSE;
  para.raw_e      = FALSE;
  para.vec_num    = (para.mfcc_dim + 1) * 2;

}

/* prepare new parameter vector holder for RealTime* */
static void
init_param()
{
  param = new_param();
  if (c0_required) {
    param->header.samptype = F_MFCC | F_ZEROTH | F_DELTA | F_CEPNORM;
  } else {
    param->header.samptype = F_MFCC | F_ENERGY | F_DELTA | F_CEPNORM;
  }
  param->header.wshift = para.smp_period * para.frameshift;
  param->header.sampsize = para.vec_num * sizeof(VECT); /* not compressed */
  param->veclen = para.vec_num;
  /* assign max (safe with free_param)*/
  param->parvec = (VECT **)mymalloc(sizeof(VECT *) * maxframelen);
  /* param->parvec */
  /* param->header.samplenum */
  /* param->samplenum */
}


/* initialize (once on startup) */
void
RealTimeInit()
{
  /* initialize MFCC computation */
  init_para();
  /* load noise spectrum for spectral subtraction from file */
  if (ssload_filename && ssbuf == NULL) {
    if ((ssbuf = new_SS_load_from_file(ssload_filename, &sslen)) == NULL) {
      j_error("Error: failed to read \"%s\"\n", ssload_filename);
    }
  }
  WMP_init(para, &bf, ssbuf, sslen);
  f_delay = para.delWin * 2 + 1;
  maxframelen = MAXSPEECHLEN / para.frameshift;
  windowlen = para.framesize + 1;
  window = mymalloc(sizeof(SP16) * windowlen);
  /* pre-load CMN from file (if needed) */
  if (cmnload_filename) {
    if (para.cmn) {
      if ((cmn_loaded = CMN_load_from_file(cmnload_filename, para.mfcc_dim))== FALSE) {
	j_printf("Warning: failed to read cepstral mean from \"%s\"\n", cmnload_filename);
      }
    } else {
      j_printf("Warning: CMN not required, file \"%s\" ignored\n", cmnload_filename);
    }
  }
}

/* prepare (on start of every input segment) */
void
RealTimePipeLinePrepare()
{
  /* prepare param */
  init_param();
  /* pre-fetch outprob cache for maximum length */
  outprob_prepare(maxframelen);
  /* check param coherence here */
  if (!check_param_coherence(hmminfo, param)) {
    need_attach = TRUE;
    if (select_param_vmark(param, hmminfo->opt.param_type) == FALSE) {
      j_error("cannot attach Param!\n");
    }
    param->header.samptype = hmminfo->opt.param_type;
  }
  /* initialize pointer */
  f_raw = 0;
  f = 0;
  windownum = 0;
}

/* main rountine (called for each incoming short segment from adin-cut) */
/* return value:
   -1 ... error -> exit, terminate 
   0  ... continue
   1  ... segmented -> exit, resume (not drop buffer)
*/
int
RealTimePipeLine(SP16 *Speech, int nowlen) /* Speech[0...nowlen] = input */
{
  int i, now;

  /* window[0..windownum-1] is left from previous call */
  
  now = 0;
  last_is_segmented = FALSE;

  if (module_mode) {
    /* tell modules to start recording/processing */
    if (f_raw == 0) msock_status_recstart();
  }

#ifdef RDEBUG
  printf("got %d samples\n", nowlen);
#endif

  /* if needed, append the incoming samples to a file */
  if (record_dirname != NULL) {
    record_sample_write(Speech, nowlen);
  }

  while (now < nowlen) {	/* till whole input is processed */
    /* fill as many as possible */
    for(i = min(windowlen - windownum, nowlen - now); i > 0 ; i--)
      window[windownum++] = (float) Speech[now++];
    if (windownum < windowlen) break; /* shortage or last */
#ifdef RDEBUG
    /*    printf("%d used, %d rest\n", now, nowlen - now);

	  printf("[f_raw = %d, f = %d]\n", f_raw, f);*/
#endif

    /* needed samples -> bf[] */
    for (i=0; i < windowlen; i++) {
      bf[i+1] = (float) window[i];
    }

    /* calc wave -> MFCC_E_Z */
    /* (bogus) needs conversion here if integerized */
    param->parvec[f_raw] = (VECT *)mymalloc(sizeof(VECT) * para.vec_num);
    WMP_calc(param->parvec[f_raw], bf, para, ssbuf);
    
    if (f_raw >= para.delWin) {
      /* delayed calc of MFCC_E -> MFCC_E_D_Z */
      /* (bogus) needs conversion here if integerized */
      WMP_Delta(param->parvec, f_raw-para.delWin, f_raw+1, para);
    }
    if (f_raw >= f_delay) {
      /* more-delayed calc of CMN: MFCC_E_D_Z -> MFCC_E_D_N_Z */
      f = f_raw - f_delay;
      if(para.cmn) CMN_realtime(param->parvec[f], para.mfcc_dim);
      if (need_attach) {
	param->veclen = exec_exclude_one_vector(param->parvec[f], para.vec_num);
	param->header.sampsize = param->veclen * sizeof(VECT);
      }

      /* now we got MFCC parameter for frame 'f', proceed search */
      /* proceed beam for 1 frame */
      if (f == 0) {
	get_back_trellis_init(param, wchmm, &backtrellis);
      }
      if (get_back_trellis_proceed(f, param, wchmm, &backtrellis, FALSE) == FALSE) {
	/* segmented, end procs ([0..f-1])*/
	/* notice: f-1 is end point, but MFCC is calced till f_raw */
	last_is_segmented = TRUE;
	last_time = f-1;
#ifdef SP_BREAK_CURRENT_FRAME
	param->header.samplenum = f_raw+1;/* len = lastid + 1 */
	param->samplenum = f_raw+1;
	rest_len = nowlen - now;
	if (rest_len > 0) {
	  /* copy rest samples to rest_Speech */
	  if (rest_Speech == NULL) {
	    rest_alloc_len = rest_len;
	    rest_Speech = (SP16 *)mymalloc(sizeof(SP16)*rest_alloc_len);
	  } else if (rest_alloc_len < rest_len) {
	    rest_alloc_len = rest_len;
	    rest_Speech = (SP16 *)myrealloc(rest_Speech, sizeof(SP16)*rest_alloc_len);
	  }
	  memcpy(rest_Speech, &(Speech[now]), sizeof(SP16) * rest_len);
	}
#else
	param->header.samplenum = f;
	param->samplenum = f;
#endif
	return(1);		/* tell parent to be segmented by this function */
      }
    }

    /* shift window */
    memmove(window, &(window[para.frameshift]), sizeof(SP16) * (windowlen - para.frameshift));
    windownum -= para.frameshift;
    f_raw++;
  }

  /* input segment is fully computed */
  return(0);			/* continue input */
}

#ifdef SP_BREAK_CURRENT_FRAME
/* process rest_param from previous segment before input start */
/* return value:
   -1 ... error -> exit, terminate 
   0  ... continue
   1  ... segmented -> exit, resume (not drop buffer)
*/
int
RealTimeResume()
{
  int t;
  int last_now;

  param = rest_param;
  
  /* prepare param by expanding the last input param */
  outprob_prepare(maxframelen);
  param->parvec = (VECT **)myrealloc(param->parvec, sizeof(VECT *) * maxframelen);
  /* set pointer for resuming */
  f_raw = param->samplenum - 1;
  if (f_raw >= f_delay) {
    /* there are left params in previous call, so recognize it first */
    f = f_raw - f_delay;
#ifdef RDEBUG
    printf("Resume: f=%d,f_raw=%d\n", f, f_raw);
#endif
    get_back_trellis_init(param, &backtrellis);
    for (t=0;t<=f;t++) {
      if (get_back_trellis_proceed(t, param, wchmm, &backtrellis, FALSE) == FALSE) {
	/* segmented, end procs ([0..f])*/
	last_is_segmented = TRUE;
	last_time = t-1;
	return(1);		/* segmented by this function */
      }
    }
  } else {
    f = 0;
  }
  
  /* do last shift */
  memmove(window, &(window[para.frameshift]), sizeof(SP16) * (windowlen - para.frameshift));
  windownum -= para.frameshift;
  f_raw++;

  if (rest_len > 0) {
    /* there are samples left in previous call, so recognize it first */
#ifdef RDEBUG
    printf("Resume: rest %d samples\n", rest_len);
#endif
    return(RealTimePipeLine(rest_Speech, rest_len));
  }
  return 0;
}
#endif /* SP_BREAK_CURRENT_FRAME */


/* end realtime procedure and return obtained param */
HTK_Param *
RealTimeParam(LOGPROB *backmax)
{
  if (last_is_segmented) {
    /* already segmented in RealTimePipeLine() */
    *backmax = finalize_1st_pass(&backtrellis, winfo, last_time);
#ifdef SP_BREAK_CURRENT_FRAME
    finalize_segment(&backtrellis, param, last_time);
#endif
    /* update CMN vector for next speech */
    if(para.cmn) CMN_realtime_update();
    /* return obtained parameter for 2nd pass */
    return(param);
  }
  /* flush last samples */
  for (f = f_raw - para.delWin; f < f_raw; f++){
    WMP_Delta(param->parvec, f, f_raw, para);
  }
  for (f = f_raw - f_delay; f < f_raw; f++) {
    if(para.cmn) CMN_realtime(param->parvec[f], para.mfcc_dim);
    if (need_attach) {
      param->veclen = exec_exclude_one_vector(param->parvec[f], para.vec_num);
      param->header.sampsize = param->veclen * sizeof(VECT);
    }
    get_back_trellis_proceed(f, param, wchmm, &backtrellis, FALSE);
  }
  /* end procedure of 1st pass */
  param->header.samplenum = f_raw;
  param->samplenum = f_raw;
  get_back_trellis_end(param, wchmm, &backtrellis);
  *backmax = finalize_1st_pass(&backtrellis, winfo, param->samplenum);
#ifdef SP_BREAK_CURRENT_FRAME
  finalize_segment(&backtrellis, param, param->samplenum);
#endif

  /* update CMN vector for next speech */
  if(para.cmn) {
    CMN_realtime_update();
    if (cmnsave_filename) {
      if (CMN_save_to_file(cmnsave_filename) == FALSE) {
	j_printf("Warning: failed to save cmn data to \"%s\"\n", cmnsave_filename);
      }
    }
  }

  /* return obtained parameter for 2nd pass */
  return(param);
}
