// file: extract_feature/extf_algo_4.cc
//

// isip include files
//
#include "extract_feature.h"
#include <Signal.h>

#include "extract_feature_constants.h"
#include <signal_constants.h>
#include <CircularBuffer.h>

// method: compute_delta_cc
//
// arguments:
//  float_8* vector_a: (input/output) feature vector
//  int_4 num_coeffs: number of coefficients in one vector used in computing 
//  int_4 dw:  (input) delta window size
//  logical_1 delta_delta: (input) if computing delta-delta coefficients
//  int_4 flag: (input) indicate EXTF_UTT_START/DURING/END
//
// this method computes delta parameters of the signal using the
// regression formula as in the equation 5.15 in the HTK Manual P72
//
logical_1 Extract_feature::compute_delta_cc(float_8* vector_a,
					    int_4 num_coeffs_a,
					    int_4 dw_a,
					    logical_1 delta_delta_a,
					    int_4 flag_a) {
  
  int_4 cb_size = dw_a * 2 + 1;

  // Circular buffer for feature data
  //
  static Circular_buffer cb1(num_coeffs_a, cb_size);

  // Circular buffer for delta
  //
  static Circular_buffer cb2(num_coeffs_a, cb_size);

  int_4 c_pos = 0;  // index for original coefficient in the vectors array
  int_4 d_pos = num_coeffs_a;  // index for delta coefficient in the vectors array
  int_4 i, dim, index;
  float_8 denom = 0.0;
  float_8 acc = 0.0;
  
  // compute the denominator of the regression formula 
  //
  for (i = 1; i<= dw_a; i++) {
    denom += i*i;
  }
  denom *= 2.0;

  // process on different cases
  //
  switch (flag_a) {
  case EXTF_UTT_START:
    
    // clean circular buffer
    //
    cb1.empty_cc();
    cb2.empty_cc();

    // push the feature data into circular buffer
    //
    for (dim = 0; dim < num_coeffs_a; dim++) {
      cb1.add_cc(dim, vector_a[dim]);
    }

    
    break;
    
  case EXTF_UTT_DURING:
    
    // push the feature data into circular buffer
    //
    for (dim = 0; dim < num_coeffs_a; dim++) {
      cb1.add_cc(dim, vector_a[c_pos + dim]);
    }

    // if data is enough to compute delta coefficient
    //
    if (cb1.get_size_cc(0) <= dw_a) {
      return ISIP_FALSE;
    }

    // initilize index to point to the current element in circular
    // buffer
    //    
    index = -dw_a;
    
    // compute delta coefficient according to regression formula
    //
    for (dim = 0; dim < num_coeffs_a; dim++) {
      
      acc = 0.0;

      for (i = 1; i <= dw_a; i++) {
	
	// take care of the beginning frames of the feature vectors
	//
	if (cb1.get_size_cc(dim) < cb_size) {
	  acc += i*(cb1(dim, index + i) - cb1(dim, 1 - cb1.get_size_cc(dim)));
	}
	else {
	  acc += i*(cb1(dim, index + i) - cb1(dim, index - i));
	}
      }

      // save the current element and delta coeficient to output
      // vector
      //
      vector_a[c_pos + dim] = delta_delta_a ?
	cb1(dim, index - dw_a) : cb1(dim, index);
      vector_a[d_pos + dim] = acc / denom;
    } // end of computing delta coefficient

    break;

  case EXTF_UTT_END:

    // if data is enough to compute delta coefficient
    //
    if (cb1.get_size_cc(0) <= dw_a + 1) {
      for (dim = 0; dim < num_coeffs_a; dim++) {
	cb1.sub_cc(dim);
	index = dw_a + 1 - cb1.get_size_cc(0);
	vector_a[c_pos + dim] = cb1(dim, index - dw_a);
      }
      break;
    }
    
    // no input element, so remove the front element and replace it as
    // the back element
    //
    for (dim = 0; dim < num_coeffs_a; dim++) {
      cb1.sub_cc(dim);
      cb1.write_cc(dim, -cb1.get_size_cc(dim), cb1(dim, 0));
    }

    // initilize index to point to the current element in circular
    // buffer
    //        
    index = dw_a + 1 - cb1.get_size_cc(0);

    // compute delta coefficient according to regression formula
    //
    for (dim = 0; dim < num_coeffs_a; dim++) {
      
      acc = 0.0;

      for (i = 1; i <= dw_a; i++) {
	acc += i*(cb1(dim, index + i) - cb1(dim, index - i));
      }

      // save the current element to output vector and save the delta
      // coefficient to output vector and delta circular buffer
      //
      vector_a[c_pos + dim] = delta_delta_a ?
	cb1(dim, index - dw_a) : cb1(dim, index);
      //      vector_a[c_pos + dim] = cb1(dim, index - dw_a);
      vector_a[d_pos + dim] = acc / denom;     
      flag_a = EXTF_UTT_DURING;
    } // end of computing delta coefficient
      
    break;

  default:
    return ISIP_FALSE;
  }

  // if compute delta-delta coefficient
  //
  if (delta_delta_a) {
  
  // update c_pos and d_pos to compute delta-delta coefficient
  //
    c_pos += num_coeffs_a;
    d_pos += num_coeffs_a;

  // process on different cases
  //
  switch (flag_a) {

  case EXTF_UTT_DURING:
    
    // push the feature data into circular buffer
    //
    for (dim = 0; dim < num_coeffs_a; dim++) {
      cb2.add_cc(dim, vector_a[c_pos + dim]);
    }

    // if data is enough to compute delta coefficient
    //
    if (cb2.get_size_cc(0) <= dw_a) {
      return ISIP_FALSE;
    }

    // initilize index to point to the current element in circular
    // buffer
    //    
    index = -dw_a;
    
    // compute delta coefficient according to regression formula
    //
    for (dim = 0; dim < num_coeffs_a; dim++) {
      
      acc = 0.0;

      for (i = 1; i <= dw_a; i++) {
	
	// take care of the beginning frames of the feature vectors
	//
	if (cb2.get_size_cc(dim) < cb_size) {
	  acc += i*(cb2(dim, index + i) - cb2(dim, 1 - cb2.get_size_cc(dim)));
	}
	else {
	  acc += i*(cb2(dim, index + i) - cb2(dim, index - i));
	}
      }

      // save the current element and delta coeficient to output
      // vector
      //
      vector_a[c_pos + dim] = cb2(dim, index);
      vector_a[d_pos + dim] = acc / denom;
    } // end of computing delta coefficient

    break;

  case EXTF_UTT_END:

    // if data is enough to compute delta coefficient
    //
    if (cb2.get_size_cc(0) <= dw_a + 1) {
      break;
    }
    
    // no input element, so remove the front element and replace it as
    // the back element
    //
    for (dim = 0; dim < num_coeffs_a; dim++) {
      cb2.sub_cc(dim);
      cb2.write_cc(dim, -cb2.get_size_cc(dim), cb2(dim, 0));
    }

    // initilize index to point to the current element in circular
    // buffer
    //        
    index = dw_a + 1 - cb2.get_size_cc(0);

    // compute delta coefficient according to regression formula
    //
    for (dim = 0; dim < num_coeffs_a; dim++) {
      
      acc = 0.0;

      for (i = 1; i <= dw_a; i++) {
	acc += i*(cb2(dim, index + i) - cb2(dim, index - i));
      }

      // save the current element to output vector and save the delta
      // coefficient to output vector and delta circular buffer
      //
      vector_a[c_pos + dim] = cb2(dim, index);
      vector_a[d_pos + dim] = acc / denom;     
      flag_a = EXTF_UTT_DURING;
    } // end of computing delta coefficient
      
    break;

  default:
    return ISIP_FALSE;
  }

  } // end of if compute delta-delta
    
  // if there is valid full feature data output
  //
  if (flag_a == EXTF_UTT_DURING) {
  
    // return without error
    //
    return ISIP_TRUE;
  }
  else {
    return ISIP_FALSE;
  }
}
