// file: $isip_ifc/class/sp/SignalDetector/sigd_10.cc
// version: $Id: sigd_10.cc 10210 2005-08-12 20:24:51Z stanley $
//

// isip include files
//
#include "SignalDetector.h"

// method: computeEndpoints
//
// arguments:
//  PRECISION precision: (input) the mode can be rough,coarse and fine
//  long channel_index: (input) audio channel index 
//
// return:  a boolean value indicating status
//
boolean SignalDetector::computeEndPoints(PRECISION precision_a, long channel_index_a) {
  
  if (precision_a == COARSE) {
    
    // scan for the start of an utterance
    //
    utt_coarse_beg_d(channel_index_a) = egy_rlse_d(channel_index_a);
    if (egy_rlse_d(channel_index_a) == (Long)0) {
      while (egy_d(channel_index_a)(utt_coarse_beg_d(channel_index_a)) != SM_STATE_SIGN) {
	utt_coarse_beg_d(channel_index_a) = (long)utt_coarse_beg_d(channel_index_a) + 1;
      }
      
      // scan for the end of an utterance
      //
      utt_coarse_end_d(channel_index_a) = (long)utt_coarse_beg_d(channel_index_a) + 1;
      while (egy_d(channel_index_a)(utt_coarse_end_d(channel_index_a)) == SM_STATE_SIGN) {
	utt_coarse_end_d(channel_index_a) = (long)utt_coarse_end_d(channel_index_a) + 1;
      }
    }
    else {
      while (egy_d (channel_index_a)(utt_coarse_beg_d(channel_index_a)-egy_rlse_d(channel_index_a)) != SM_STATE_SIGN) {
	utt_coarse_beg_d(channel_index_a) = (long)utt_coarse_beg_d(channel_index_a) + 1;
      }
      
      // scan for the end of an utterance
      //
      utt_coarse_end_d (channel_index_a)= (long)utt_coarse_beg_d (channel_index_a)+ 1;
      while (egy_d (channel_index_a) (utt_coarse_end_d(channel_index_a)-egy_rlse_d(channel_index_a)) == SM_STATE_SIGN) {
	utt_coarse_end_d(channel_index_a) = (long) utt_coarse_end_d(channel_index_a) + 1;
      }
    }
    
    // exit gracefully
    //
    return true;
    
  }
  
  
  if (precision_a == MEDIUM) {
    
    // scan from the beginning of utterance backwards to the first NOIS state
    //
    if (egy_rlse_d(channel_index_a) == (Long)0) {
      while (egy_d(channel_index_a)(utt_coarse_beg_d(channel_index_a)) != SM_STATE_NOIS && utt_coarse_beg_d(channel_index_a) >(Long)0){
	utt_coarse_beg_d(channel_index_a) = (long)utt_coarse_beg_d(channel_index_a) - 1;
      }
      
      // scan from the end of an utterance forward to the first NOIS state
      //
      utt_coarse_end_d(channel_index_a)= (long)utt_coarse_end_d(channel_index_a) + 1;
      while (egy_d(channel_index_a)(utt_coarse_end_d(channel_index_a)) != SM_STATE_NOIS && utt_coarse_end_d(channel_index_a) < egy_cur_d(channel_index_a) ) {
	utt_coarse_end_d(channel_index_a)= (long) utt_coarse_end_d(channel_index_a) + 1;
      }
    }
    else {
      while (egy_d(channel_index_a)(utt_coarse_beg_d(channel_index_a)-egy_rlse_d(channel_index_a)) != SM_STATE_NOIS && utt_coarse_beg_d(channel_index_a) >(Long)0){
	utt_coarse_beg_d(channel_index_a) = (long)utt_coarse_beg_d(channel_index_a) - 1;
      }
      
      // scan from the end of an utterance forward to the first NOIS state
      //
      utt_coarse_end_d(channel_index_a) = (long) utt_coarse_end_d(channel_index_a) + 1;
      while (egy_d(channel_index_a)(utt_coarse_end_d(channel_index_a)-egy_rlse_d(channel_index_a)) != SM_STATE_NOIS && utt_coarse_end_d(channel_index_a) < egy_cur_d (channel_index_a)) {
	utt_coarse_end_d(channel_index_a) = (long)utt_coarse_end_d(channel_index_a) + 1; 
      }
    }
    
    utt_egy_beg_d(channel_index_a) = (long)num_frame_d(channel_index_a) - ((abs(egy_cur_d(channel_index_a) - utt_coarse_beg_d(channel_index_a))));
    utt_egy_end_d(channel_index_a) = (long)num_frame_d(channel_index_a) - ((abs(egy_cur_d(channel_index_a) - utt_coarse_end_d(channel_index_a))));
    // exit gracefully
    //
    return true;
  }
  
  if (precision_a == FINE) {
    
    //initialize and define local variables
    //
    long count = 0;
    long index_p;
    long i;
    
    // loop back 5 frames to check if zerocrossings increase a threshold
    // so that endpoints can be refinedend_time 
    //
    if( utt_egy_beg_d(channel_index_a)-(Long)5 >= egy_rlse_d(channel_index_a)) {
      
      for ( i = 0; i < 5; i++) {
	index_p = (long)utt_egy_beg_d(channel_index_a) - 1;
	if (zc_d(channel_index_a)(index_p)  >= zc_utterance_thresh_d ) {
	  count++;
	}
      }
      
      // if number of frames with zcrate greater than the threshold is greater 
      // than 3, take the last checked frame as utterance beginning or 
      // utterance end as applicable
      //
      if (count > 3) {
	utt_zc_beg_d(channel_index_a) = utt_egy_beg_d(channel_index_a) -(Long) 5;
      }
      else {
	utt_zc_beg_d(channel_index_a) = utt_egy_beg_d(channel_index_a);
      }
    }
    count = 0 ;
    
    if (utt_egy_end_d(channel_index_a) + (Long)5 <= egy_cur_d(channel_index_a)) {
      
      for ( i = 0; i < 5 ; i++) {
	index_p = (long)utt_egy_end_d(channel_index_a) + 1;
	if (zc_d(channel_index_a)(index_p)  >= zc_utterance_thresh_d) {
	  count++;
	}
      }
      
      if (count > 3) {
	utt_zc_end_d(channel_index_a) = (long)utt_egy_end_d(channel_index_a) + 2;
      }
      else {
	utt_zc_end_d(channel_index_a) = utt_egy_end_d(channel_index_a);
      } 
    }
    // exit gracefully
    //
    return true;
  }
  
  return false;
  
}


// method: getEndPoints
//
// arguments:
//  float& t1: (output) start time of utterance
//  float& t2: (output) end time of utterance
//  long channel_index: (input) audio channel index 
//
// return: boolean
//
boolean SignalDetector::computeEndTime(float& t1_a, float& t2_a,
				       long channel_index_a) {
  
  // check if valid endpoints exist
  //
  if (utt_egy_beg_d(channel_index_a) < (Long)0) {
    return false;
  }
  
  // convert the endpoints to time
  //
  t1_a = (long)utt_egy_beg_d(channel_index_a) * frame_duration_d;
  t2_a = (long)utt_egy_end_d(channel_index_a) * frame_duration_d;
  // pad beginning time
  //
  t1_a -= pad_time_d;
  if (t1_a < 0) {
    t1_a = 0;
  }
  
  // pad ending time
  //
  t2_a += pad_time_d;
  float total_data_time = (float)num_frame_d(channel_index_a) * frame_duration_d ;
  if (t2_a > total_data_time) {
    t2_a = total_data_time;
  }
  
  // exit gracefully
  //
  return true;
}


// method: updateThresholds
//
// arguments:
//  double egy: (input) energy in dB
//
// return: a boolean value indicating the status
//
boolean SignalDetector::updateThresholds(double egy_a) {
  
  // update the noise threshold
  //
  if (egy_a > noise_floor_d) {
    if (egy_a < (noise_nominal_level_d + noise_adaptation_delta_d)) {
      noise_threshold_d = noise_adaptation_constant_d * noise_threshold_d +
	(1.0 - noise_adaptation_constant_d) * egy_a;
    }
  }
  
  // update the signal threshold
  //
  if (egy_a > (signal_nominal_level_d - signal_adaptation_delta_d)) {
    signal_threshold_d = signal_adaptation_constant_d * signal_threshold_d +
      (1.0 - signal_adaptation_constant_d) * egy_a;
  }
  
  // exit gracefully
  //
  return true;
}
