// file: $isip_ifc/class/sp/SignalDetector/SignalDetector.h
// version: $Id: SignalDetector.h 10214 2005-08-15 05:01:23Z stanley $
//

// make sure definitions are only made once
//
#ifndef ISIP_SIGNAL_DETECTOR
#define ISIP_SIGNAL_DETECTOR

// isip include files
//
#ifndef ISIP_VECTOR
#include <Vector.h>
#endif

#ifndef ISIP_CIRCULAR_BUFFER
#include <CircularBuffer.h>
#endif

#ifndef ISIP_VECTOR_FLOAT
#include <VectorFloat.h>
#endif

#ifndef ISIP_BOOLEAN
#include <Boolean.h>
#endif

#ifndef ISIP_DEBUG_LEVEL
#include <DebugLevel.h>
#endif

// SignalDetector: This class is used to detect the start and stop
// times of a segment of data in a stream of data. The audio data,
// which is represented as a multichannel sequence of floating point
// data is handled as a vector of vector floats. The audio data is
// passed to this class in variable-sized amounts of data. Methods
// are provided to determine the state of the detection, including when
// a valid signal has been detected.
//
class SignalDetector {
  
  //---------------------------------------------------------------------------
  //
  // public constants
  //
  //---------------------------------------------------------------------------
public:
  
  // define the class name
  //
  static const String CLASS_NAME;
  
  //----------------------------------------
  //
  // other important constants
  //
  //----------------------------------------
  
  // define the algorithm choices
  //
  enum ALGORITHM { ENERGY = 0, ENERGY_ZC , DEF_ALGORITHM = ENERGY };
  
  // define the implementation choices
  //
  enum IMPLEMENTATION { DB_POWER = 0, DEF_IMPLEMENTATION = DB_POWER };
  
  // define the precision of the endpoints:
  //  the endpoints are successively refined
  //
  enum PRECISION { COARSE = 0, MEDIUM, FINE, DEF_PRECISION = COARSE };
  
  // define the static objects
  //
  static const NameMap ALGO_MAP;
  static const NameMap IMPL_MAP;
  
  //----------------------------------------
  //
  // i/o related constants
  //
  //----------------------------------------
  
  // constants relating to implementation and algorithm.
  //
  static const String DEF_PARAM;
  static const String PARAM_ALGORITHM;
  static const String PARAM_IMPLEMENTATION;
  
  // constants relating to signal processing
  //
  static const String PARAM_NUM_CHANNELS;
  static const String PARAM_SAMPLE_FREQUENCY;
  static const String PARAM_SAMPLE_NUM_BYTES;
  static const String PARAM_CHANNEL_TO_BE_PROCESSED;  
  static const String PARAM_FRAME_DURATION;
  static const String PARAM_WINDOW_DURATION;
  
  // constants relating to preprocessing of the audio data
  //
  static const String PARAM_PREEMPHASIS;
  
  // constants related to the output signal
  //
  static const String PARAM_PAD_TIME;
  
  // this section contains data common to all algorithms:
  //  the basic assumption in this class is that the signal
  //  and noise have different durational properties
  //
  
  // constants relating to the energy levels
  //
  static const String PARAM_SIGNAL_NOMINAL_LEVEL;
  static const String PARAM_SIGNAL_ADAPTATION_DELTA;
  static const String PARAM_SIGNAL_ADAPTATION_CONSTANT;
  
  // constants relating to noise levels
  //
  static const String PARAM_NOISE_NOMINAL_LEVEL;
  static const String PARAM_NOISE_ADAPTATION_DELTA;
  static const String PARAM_NOISE_ADAPTATION_CONSTANT;
  static const String PARAM_NOISE_FLOOR;
  
  // constants related to utterance duration and separation
  //
  static const String PARAM_UTTERANCE_MINIMUM_DURATION;
  static const String PARAM_UTTERANCE_MAXIMUM_DURATION;
  static const String PARAM_UTTERANCE_MINIMUM_SEPARATION;
  static const String PARAM_UTTERANCE_DELTA;
  
  // this section contains data for a specific algorithm
  //
  
  // algorithm: energy_zc
  // implementation: db_power
  // description: combine a zero-crossing measure with energy
  //
  static const String PARAM_ZC_UTTERANCE_THRESH;
  static const String PARAM_ZC_NEGATIVE_THRESH;
  static const String PARAM_ZC_POSITIVE_THRESH;
  
  // constants for debug level
  //
  static const String PARAM_DBGL;
  
  //----------------------------------------
  //
  // default values and arguments
  //
  //---------------------------------------- 
  
  // constants relating to signal processing
  //
  static const long DEF_NUM_CHANNELS = 1;
  static const float DEF_SAMPLE_FREQUENCY = 16000.0;
  static const long DEF_SAMPLE_NUM_BYTES = 2;
  static const long DEF_CHANNEL_TO_BE_PROCESSED = 1;
  static const float DEF_FRAME_DURATION = 0.020;
  static const float DEF_WINDOW_DURATION  = 0.030;
  static const long DEF_CBUF_LENGTH  = 3000000;
  
  // constants relating to preprocessing of the audio data
  //
  static const float DEF_PREEMPHASIS = 0.95;
  
  // constants related to the output signal
  //
  static const float DEF_PAD_TIME = 0.10;
  
  // this section contains data common to all algorithms:
  // the basic assumption in this class is that the signal
  // and noise have different durational properties
  //
  
  // constants relating to the energy levels
  //
  static const float DEF_SIGNAL_NOMINAL_LEVEL = -30.0;
  static const float DEF_SIGNAL_ADAPTATION_DELTA = 5.0;
  static const float DEF_SIGNAL_ADAPTATION_CONSTANT = 0.50;
  
  // constants relating to noise levels
  //
  static const float DEF_NOISE_NOMINAL_LEVEL = -40.0;
  static const float DEF_NOISE_ADAPTATION_DELTA = 5.0;
  static const float DEF_NOISE_ADAPTATION_CONSTANT = 0.75;
  static const float DEF_NOISE_FLOOR = -80.0;
  
  // constants related to utterance duration and separation
  //
  static const float DEF_UTTERANCE_MINIMUM_DURATION = 0.2;
  static const float DEF_UTTERANCE_MINIMUM_SEPARATION = 0.1;
  static const float DEF_UTTERANCE_MAXIMUM_DURATION = 100.0;
  static const float DEF_UTTERANCE_DELTA = 10.0;
  
  // state machine related parameters:
  //  the basic approach in this class is to use a state
  //  machine to keep track of the previous state of the
  //  process. we use a state machine with four states
  //  and we use the three previous states to make decisions
  //
  static const long SM_NSTATES = 4;
  static const long SM_HISTORY = 3;
  static const long SM_STATE_NOIS = 0;
  static const long SM_STATE_NTOS = 1;
  static const long SM_STATE_STON = 2;
  static const long SM_STATE_SIGN = 3;
  
  static const float SM_WGT_NOIS = 0.0;
  static const float SM_WGT_SIGN = 1.0;
  static const float SM_WGT_TRAN = 0.5;
  
  // sample value scaling
  // 1/DEF_SAMPLE_SCALE_FACTOR = 32767.  So we
  // normalize the sample values
  //
  static const double DEF_SAMPLE_SCALE_FACTOR = 3.051850e-05;
  
  // this section contains data for a specific algorithm
  //
  
  // algorithm: energy_zc
  // implementation: db_power
  // description: combine a zero-crossing measure with energy
  //
  static const long DEF_ZC_UTTERANCE_THRESH = 0;
  static const long DEF_ZC_POSITIVE_THRESH = 0;
  static const long DEF_ZC_NEGATIVE_THRESH = 0;
  
  //----------------------------------------
  //
  // error codes
  //
  //----------------------------------------  
  
  static const long ERR = 80500;
  static const long ERR_AUDIO_BUFFER_OVERFLOW = 80510;
  static const long ERR_ENERGY_BUFFER_OVERFLOW = 80520;
  static const long ERR_ZC_BUFFER_OVERFLOW = 80530;
  static const long ERR_OUT_OF_BOUNDS = 80540;
  
  //---------------------------------------------------------------------------
  //
  // protected data
  //
  //---------------------------------------------------------------------------
protected:
  
  // algorithm name
  //
  ALGORITHM algorithm_d;
  
  // implementation name
  //
  IMPLEMENTATION implementation_d;
  
  // static memory manager
  //
  static MemoryManager mgr_d;
  
  // debugging parameters
  //
  DebugLevel debug_level_d;
  
  
  // this flag is used to determine if the object's init method
  // needs to be called
  //
  boolean is_valid_d;
  
  
  // circular buffer which is used to hold audio data
  //
  Vector<CircularBuffer<Float> > cbuf_d;
  
  //----------------------------------------------------
  //
  // define parameters that are written to the parmeter file
  //
  //----------------------------------------------------
  
  // variables relating to signal processing
  //
  Long num_channels_d;
  Float sample_frequency_d;
  Long sample_num_bytes_d;
  Long channel_to_be_processed_d;
  Float frame_duration_d;
  Float window_duration_d;
  
  
  // variables relating to preprocessing of the audio data
  //
  Float preemphasis_d;
  
  // variable related to the output signal
  //
  Float pad_time_d;
  
  // signal level-related energy parameters
  //
  Float signal_nominal_level_d;
  Float signal_adaptation_delta_d;
  Float signal_adaptation_constant_d;
  
  // noise level-related energy parameters
  //
  Float noise_nominal_level_d;
  Float noise_adaptation_delta_d;
  Float noise_adaptation_constant_d;
  Float noise_floor_d;
  
  // utterance-related parameters
  //
  Float utterance_minimum_duration_d;
  Float utterance_maximum_duration_d;
  Float utterance_minimum_separation_d;
  Float utterance_delta_d; 
  
  // adaptive thresholds
  //
  Float noise_threshold_d;
  Float signal_threshold_d;
  
  // these parameters are calculated from the values calculated from
  // the parameter file
  //
  
  // variables relating to signal processing
  //
  long frame_sample_duration_d;
  long window_sample_duration_d;
  
  // utterance-related parameters
  //
  long utterance_minimum_sample_duration_d;
  long utterance_minimum_sample_separation_d;
  
  
  //----------------------------------------------------
  //
  // define parameters that are common to all algorithms
  // and implementations
  //
  //----------------------------------------------------
  
  // state machine
  //
  Vector<VectorLong> states_d;
  Vector<VectorLong> durations_d;
  
  // useful counters
  //
  Vector<Long> num_frame_d;
  
  // define variables to handle the energy values
  //
  Vector<CircularBuffer<Long> > egy_d;
  
  // length of the energy buffer
  //
  long egy_len_d;
  Vector<Long> egy_cur_d;
  Vector<Long> egy_rlse_d;
  Vector<VectorFloat> egy_data_window_d;
  
  // energy patterns used to detect signal or noise
  //
  Vector<Long> sig_patn_d;
  long sig_patn_len_d;
  Vector<Long> nse_patn_d;
  long nse_patn_len_d;
  
  
  // information that tracks the state of the detection process
  //
  Vector<Boolean> utt_in_progress_d;
  
  // keeps track of the number of endpoints
  //
  Vector<Long> num_endpoints_d;
  
  Vector<VectorFloat> start_points_d;
  Vector<VectorFloat> stop_points_d;
  Vector<Long> utt_coarse_beg_d;
  Vector<Long> utt_coarse_end_d;
  Vector<Long> utt_egy_beg_d;
  Vector<Long> utt_egy_end_d;
  Vector<Long> utt_zc_beg_d;
  Vector<Long> utt_zc_end_d;
  
  // algorithm: energy_zc
  // implementation: db_power
  // description: combine a zero-crossing measure with energy
  //
  
  // define variables to handle the zero crossing values
  //
  Vector<CircularBuffer<Long> > zc_d;  
  Vector<VectorFloat> zc_data_window_d;
  
  Long zc_utterance_thresh_d;                            
  Long zc_negative_thresh_d;
  Long zc_positive_thresh_d;
  
  // useful scale factors
  // win_dur_scale_factor_d = 1/(number of samples in a window of data)
  // this value is used in the enrgy calculation
  //
  Float win_dur_scale_factor_d;
  
  //---------------------------------------------------------------------------
  //
  // required public methods
  //
  //---------------------------------------------------------------------------
public:
  
  // method: name
  //
  static const String& name() {
    return CLASS_NAME;
  }
  
  // other static methods
  //
  static boolean diagnose(Integral::DEBUG debug_level);
  
  // debug methods:
  //  setDebug is inherited from the base class
  //
  boolean debug(const unichar* msg) const;
  
  // method: destructor
  //
  ~SignalDetector();
  
  // method: default constructor
  //
  SignalDetector(ALGORITHM algorithm,
		 IMPLEMENTATION implementation) {
    
    algorithm_d = algorithm;
    implementation_d = implementation;
    is_valid_d = false;
  }
  
  // default constructor
  //
  SignalDetector();
  
  // method: copy constructor
  //
  SignalDetector(const SignalDetector& arg) {
    assign(arg);
  }
  
  // assign methods
  //
  boolean assign(const SignalDetector& arg);
  
  // method: operator=
  //
  SignalDetector& operator= (const SignalDetector& arg) {
    assign(arg);
    return *this;
  }
  
  // i/o methods
  //
  long sofSize() const;
  
  boolean read(Sof& sof, long tag, const String& name = CLASS_NAME);
  boolean write(Sof& sof, long tag, const String& name = CLASS_NAME) const;
  
  boolean readData(Sof& sof, const String& pname = DEF_PARAM,
		   long size = SofParser::FULL_OBJECT,
		   boolean param = true,
                   boolean nested = false);
  boolean writeData(Sof& sof, const String& pname = DEF_PARAM) const;
  
  // equality methods
  //
  boolean eq(const SignalDetector& arg) const;
  
  // method: new
  //
  static void* operator new(size_t size) {
    return mgr_d.get();
  }
  
  // method: new[]
  //
  static void* operator new[](size_t size) {
    return mgr_d.getBlock(size);
  }
  
  // method: delete
  //
  static void operator delete(void* ptr) {
    mgr_d.release(ptr);
  }
  
  // method: delete[]
  //
  static void operator delete[](void* ptr) {
    mgr_d.releaseBlock(ptr);
  }
  
  // method: setGrowSize
  //
  static boolean setGrowSize(long grow_size) {
    return mgr_d.setGrow(grow_size);
  }
  
  // other memory management methods
  //
  boolean clear(Integral::CMODE ctype = Integral::DEF_CMODE);
  
  // method to set the parser
  //
  boolean setParser(SofParser* parser);
  
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  set methods
  //
  //---------------------------------------------------------------------------
  
  // method: setAlgorithm
  //
  boolean setAlgorithm(ALGORITHM algorithm) {
    algorithm_d = algorithm;
    is_valid_d = false;
    return true;
  }
  
  // method: setImplementation
  //
  boolean setImplementation(IMPLEMENTATION implementation) {
    implementation_d = implementation;
    is_valid_d = false;
    return true;  
  }
  
  // method: set
  //
  boolean set(ALGORITHM algorithm = DEF_ALGORITHM,
	      IMPLEMENTATION implementation = DEF_IMPLEMENTATION) {
    algorithm_d = algorithm;
    implementation_d = implementation;
    is_valid_d = false;
    return true;
  }
  
  // other set methods
  //
  boolean setdefaultParameters();
  boolean setNumChannels(long num_channels);
  boolean setSampleNumBytes(long sample_num_bytes);
  boolean setSampleFrequency(float sample_rate);
  boolean setChannelToBeProcessed(long chan);
  boolean setFrameDuration(float fram_dur);
  boolean setWindowDuration(float win_dur);
  boolean setPreemphasis(float premphasis);
  boolean setSigNominalLevel(float nom_sig_lev);
  boolean setSigAdaptDelta(float sig_delta);
  boolean setSigAdaptConst(float signal_adapt);
  boolean setNoiseNominalLevel(float nom_noise_lev);
  boolean setNoiseAdaptDelta(float noise_delta);
  boolean setNoiseAdaptConst(float noise_adapt);
  boolean setNoiseFloor(float noise_floor);
  boolean setUttDelta(float utt_delta);
  boolean setMinUttDur(float min_utt_dur);
  boolean setMinUttSep(float min_utt_sep);
  boolean setMaxUttDur(float max_utt_dur);
  boolean setZcUttThreshold(long zc_utt_thresh);
  boolean setZcNegThreshold(long zc_neg_thresh);
  boolean setZcPosThreshold(long zc_pos_thresh);
  boolean setPadTime(float pad_time);
  
  // TODO
  // boolean setUttStatus(boolean utt_status);
  
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  get methods
  //
  //---------------------------------------------------------------------------
  
  // method: getAlgorithm
  //
  ALGORITHM getAlgorithm() const {
    return algorithm_d;
  }  
  
  // method: getImplementation
  //
  IMPLEMENTATION getImplementation() const {
    return implementation_d;
  }
  
  // method: get
  //
  boolean get(ALGORITHM& algorithm,
	      IMPLEMENTATION& implementation) {
    algorithm = algorithm_d;
    implementation = implementation_d;
    return true;
  }
  
  
  // other get methods
  //
  long getNumChannels();
  long getSampleNumBytes();
  long getSampleFrequency();
  long getChannelToBeProcessed();
  float getFrameDuration();
  float getWindowDuration();
  float getPreemphasis();
  float getSigNominalLevel();
  float getSigAdaptDelta();
  float getSigAdaptConst();
  float getNoiseNominalLevel();
  float getNoiseAdaptDelta();
  float getNoiseAdaptConst();
  float getNoiseFloor();
  float getUttDelta();
  float getMinUttDur();
  float getMinUttSep();
  float getMaxUttDur();
  long getZcUttThreshold();
  long getZcNegThreshold();
  long getZcPosThreshold();
  long getPadTime();
  Vector<Boolean> getUttStatus();
  boolean getEndTime(VectorFloat& start_time, VectorFloat& end_time, long channel_index_a);
  
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  // 
  //
  //---------------------------------------------------------------------------
  
  // this method initializes the detector and clears all buffers
  //
  boolean init();
  
  // this method clears all circular buffers only if an utterance is not
  // in progress. it is used to prevent the detector from accumulating
  // large amounts of memory while it is processing noise.
  //
  boolean flush();
  
  // this method resets the detector to the start state in which it
  // can begin processing a new utterance
  //
  boolean reset(long channel_index_a);
  
  // these methods processing an incoming buffer of data. note
  // that data can be passed to this algorithm in variable sized
  // chunks. the apply method accepts multichannel data; the
  // compute method processes a single channel of data.
  //
  long apply(Vector<VectorFloat> audio_data);
  long compute(long channel_index_a);
  
  // these methods allow a user to get the current endpoints
  // while the detection algorithm is in progress. these methods
  // can be used to get the endpoints once a valid utterance has
  // been found.
  //
  boolean computeEndPoints(PRECISION precision, long channel_index_a);
  boolean computeEndTime(float& start_time, float& stop_time, long channel_index_a);
  boolean computeStartTime(float& start_time, long channel_index_a);
  
  
  //---------------------------------------------------------------------------
  //
  // private methods
  //
  //---------------------------------------------------------------------------
private:
  
  // methods common to all algorithms and implementations
  //
  
  // update all adaptive thresholds
  //
  boolean updateThresholds(double egy);
  
  // state machine related methods
  //
  boolean stateMachineReset(long channel_index_a);
  long stateMachineAdvance(float egy, long channel_index_a);
  
  // circular buffer related methods
  //
  long cbScanForPattern(Vector<Long> pattern, long len, long start_frame, long channel_index_a);
  boolean cbSmoothSignal(long channel_index_a);
  boolean cbSmoothNoise(long channel_index_a);
  boolean cbSmoothTransitions(long channel_index_a);
  boolean cbUttInProgress(long channel_index_a);
  boolean cbUttNotInProgress(long channel_index_a);
  boolean cbRemoveShortSignalBursts(long channel_index_a);
  boolean cbRemoveShortNoiseBursts(long channel_index_a);
  boolean cbRemoveLongNoiseBursts(long channel_index_a);
  boolean cbPurge(long channel_index_a);
  boolean cbRelease(long frame_index, long channel_index_a);
  boolean cbZeroCrossingRelease(long frame_index);  
  
  // description: standard energy computation
  //
  float computeEnergy(VectorFloat signal, long channel_index_a);
  
  // algorithm: energy_zc
  // implementation: db_power
  // description: combine a zero-crossing measure with energy
  //
  long computeZeroCrossingRate(VectorFloat signal, long channel_index_a);
  
};

// end of include file
// 
#endif
