// file: $isip/util/speech/isip_transform/isip_signal_detect.cc
// version: $Id: isip_signal_detect.cc 10623 2006-08-24 16:04:57Z dom5 $
//
// isip include files
//
#include "isip_signal_detect.h"

// utility: isip_signal_detect: 
//
// finds the endpoints for the input file and splits them into N
// output files based on the endpoints
//
// main program starts here
//
int main(int argc, const char** argv) {
  
  // declare the primary objects
  //
  Sdb sdb;
  CommandLine cmdl(sdb);
  Sof sof;
  AudioFile src;
  SignalDetector sig_det;
  
  // declare local variables used to handle command line options
  //
  Filename log_file;
  Filename params_file;
  String input_type;
  String output_directory;  
  String output_extension;
  String output_format;
  String input_format;
  Long output_preserve;
  String output_suffix;
  String output_type;
  String segmentation_mode;
  DebugLevel verbosity;
  
  // declare miscellaneous local variables
  //
  Filename input_file;
  String cmdl_value;
  
  //---------------------------------------------------------------------------
  //
  // configure and parse the command line:
  //  add only the options that are not included by default.
  // 
  //---------------------------------------------------------------------------
  
  // set up messages
  //
  cmdl.setUsage(
#include "usage_message.text"
       );
  cmdl.setHelp(
#include "help_message.text"
  );
  
  cmdl.setIdent("$Revision: 10623 $",
		"$Name: $",
		"$Date: 2006-08-24 11:04:57 -0500 (Thu, 24 Aug 2006) $");
  
  // add a command line option
  //
  cmdl.addOptionParam(log_file, CommandLine::DEF_PARAM_LFILE,
		      CommandLine::DEF_LFILE_NAME);
  cmdl.addOptionParam(params_file, CommandLine::DEF_PARAM_PFILE,
		      CommandLine::DEF_EMPTY);
  cmdl.addOptionParam(input_format, INPUT_FILE_FORMAT,
		      CommandLine::DEF_EMPTY);
  cmdl.addOptionParam(input_type, INPUT_FILE_TYPE, CommandLine::DEF_EMPTY); 
  cmdl.addOptionParam(output_type, OUTPUT_FILE_TYPE, CommandLine::DEF_EMPTY);
  cmdl.addOptionParam(output_format, OUTPUT_FILE_FORMAT,
		      CommandLine::DEF_EMPTY); 
  cmdl.addOptionParam(segmentation_mode, SEGMENTATION_MODE,
		      CommandLine::DEF_EMPTY);
  cmdl.addOptionParam(verbosity, CommandLine::DEF_PARAM_VERBOSITY);
  
  // parse the command line
  //
  if (!cmdl.parse(argc, argv)) {
    cmdl.printUsage();
  };
  
  //-------------------------------------------------------------------------
  //
  // override any parameter file parameters that were set
  // by command line options.
  //
  //-------------------------------------------------------------------------
  
  if (cmdl.getValue(cmdl_value, CommandLine::DEF_PARAM_DIRECTORY)) {
    output_directory.assign(cmdl_value);
  }
  
  if (cmdl.getValue(cmdl_value, CommandLine::DEF_PARAM_EXTENSION)) {
    output_extension = cmdl_value;
  }
  
  if (cmdl.getValue(cmdl_value, CommandLine::DEF_PARAM_PRESERVE)) {
    long preserve;
    cmdl_value.get(preserve);
    output_preserve.assign(preserve);
  }
  
  if (cmdl.getValue(cmdl_value, CommandLine::DEF_PARAM_SUFFIX)) {
    output_suffix = cmdl_value;
  }
  
  // check for required options and arguments
  //
  if ((params_file.length() == (long)0) ||
      (sdb.length() == (long)0)) {
    cmdl.printUsage();
  }
  
  //configure the console
  //
  if (log_file.ne(CommandLine::DEF_LFILE_NAME)) {
    Console::open(log_file);
  }
  
  // echo the command line
  //
  if (verbosity > Integral::BRIEF) {
    cmdl.printCommandLine();
    cmdl.printVersion();
  }
  
  // loop over all files: count the number of files processed
  //
  long num_files = 0;
  long num_processed = 0;
  sdb.gotoFirst();
  
  do {
    
    // fetch the next file
    //
    sdb.getName(input_file);
    num_files++;
    
    // print info if verbosity is set to BRIEF
    // or greater
    //
    if (verbosity >= Integral::BRIEF) {
      String output;
      output.assign(L"Processing file: ");
      output.concat(input_file);
      Console::put(output);
    }
    
    // check for wav format
    //
    if (input_format.eq(FORMAT_WAV)) {
      src.setFileFormat(AudioFile::WAV);  // set file format
    }
    
    // check for raw format
    //
    else if (input_format.eq(FORMAT_RAW)) {
      src.setFileFormat(AudioFile::RAW);  // set file format
      
    }
    
    // check sof format
    //
    else if (input_format.eq(FORMAT_SOF)) {
      src.setFileFormat(AudioFile::SOF);  // set file format
    }
    
    // display an error message if none of the above format
    // is specified
    //
    else {
      Console::put(L"ERROR: the input file should be in raw, wav or sof format");
      // exit gracefully
      //
      return Integral::exit();
    }
    
    // check if its is binary
    //
    if (input_type.eq(TYPE_BINARY)) {
      src.setFileType(AudioFile::BINARY);  // set file type
    }
    
    // else, set it to text
    //
    else if (input_type.eq(TYPE_TEXT)) {
      src.setFileType(AudioFile::TEXT);  // set file type
    }
   
    // opening the param file
    //
    sof.open(params_file, File::READ_ONLY);
    
    // read the param file
    //
    sig_det.read(sof, (long)0);
    
    // close the params file
    //
    sof.close();

    // get the number of channels from the signal
    // detector object and set it to the AudioFile
    // object
    //
    src.setNumChannels((long)sig_det.getNumChannels());
    
    // initialise the signal detector object using the param file
    //
    sig_det.init();
    
    // open file, default is read only
    //
    src.open(input_file);
    
    // defined the sample number for one process
    //
    long N = (long) (sig_det.getFrameDuration() *
		     sig_det.getSampleFrequency());
    
    Long len;  // define local variable
    
    len = src.getNumSamples(); // get number of samples in this file
    len = (long)len / N;      // get how many frames we need to process
    
    //******************************************************************
    // the following block does endpointing.
    // 1) reads the file as chunks
    // 2) tries to find endpoints 
    // 3) returns the endpoints as float arrays
    //******************************************************************
    
    // loop through N samples at a time
    //
    for (long i = 0; i < len; i++) {
      
      
      // define local variable to hold data and set
      // the length equal to the number of channels
      //
      Vector<VectorFloat> data((long)src.getNumChannels());
      
      // get sample data
      //
      src.getData(data,i*N, N);
      
      // call the main computational method
      //
      sig_det.apply(data);
      
    }
    
    // declare a float array that will hold the
    // start and the stop points
    //
    VectorFloat start_points;
    VectorFloat stop_points; 
    
    // get the endpoints
    //
    sig_det.getEndTime(start_points, stop_points, 0);
    
    // get the sampling frequency. We need this to convert our
    // time marks to sample values to create wav file for the
    // endpointed utterance.
    //
    long sample_frequency = sig_det.getSampleFrequency();
    
    // get the length of the whole file
    // (units - samples)
    //
    long audio_len = src.getNumSamples();
    
    // define local variable to hold data
    //
    Vector<VectorFloat> audio_data((long)src.getNumChannels());
    
    // get sample data
    //
    src.getData(audio_data, 0, audio_len);
    
    // close the file
    //
    src.close();
    
    // make sure endpoints were found
    // before we start writing chopped
    // data files.
    //
    if (start_points.length() > 0) {
      
      // the buffer that holds the chopped data
      //
      Vector<VectorFloat> chopped_audio_data;
      
      // print info if the verbosity is
      // greater than BRIEF
      //
      if (verbosity > Integral::BRIEF) {
	String output;
	output.assign(L"Generating files: ");
	Console::put(output);
      }
      
      //*****************************************************************
      // There are two mode by which the audio file can be chopped
      // Mode 1: ALL
      // Mode 2: SIGNAL_ONLY
      // Example(Mode 1):
      // Lets consider we have an audio file - foo.wav
      // The Signal Detector finds three pairs of endpoints
      // Endpoints: t1-s1, t2-s2, t3-s3
      // The way the audio file is chopped for ALL mode is:
      // foo_000.wav conatining audio data from (0 - t1)
      // foo_001.wav containing audio data from (t1 - s1)
      // foo_002.wav containing audio data from (s1 - t2)
      // foo_003.wav containing audio data from (t2 - s2)
      // foo_004.wav containing audio data from (s2 - t3)
      // foo_001.wav containing audio data from (t3 - s3)
      // foo_001.wav containing audio data from (s3 - end of audio file)
      // So for three pairs of endpoints, we get 7 choped files when
      // Mode is set to ALL mode. You can find a similar expalanation
      // for Mode SIGNAL_ONLY before the else block.
      // ****************************************************************
      
      if (segmentation_mode.eq(MODE_ALL)) {
	
	// write from that start to the first endpoint
	//
	long start_sample = 0;
	long stop_sample = (long)(start_points(0) * (float)sample_frequency);
	
	// get the chopped data
	//
	chopped_audio_data = getChoppedData(audio_data,
					    start_sample,
					    stop_sample);
	// write to a file
	//
	Filename file_name = writeChoppedData(chopped_audio_data,
					      output_format, input_format,
					      output_type, output_suffix,
					      output_extension,
					      input_file, output_directory,
					      output_preserve,
					      sample_frequency,
					      verbosity); 
	
	// if verbosity is greater than BRIEF output the
	// endpoints
	//
	if (verbosity > Integral::BRIEF) {
	  String output;
	  output.concat(L"[");
	  output.concat(L"0.0, ");
	  output.concat((float)start_points(0));
	  output.concat(L" ]  ");
	  output.concat(file_name);
	  Console::putNoWrap(output);
	}
	
	// we get the number of endpoints from the length of the
	// startpoints vector. We loop that many times and write
	// enpointed wave files every loop
	//
	for(long i=0; i<start_points.length(); i++) {
	  
	  // get the start and the stop sample
	  //
	  long start_sample = (long)(start_points(i) *
				     (float)sample_frequency);
	  
	  long stop_sample = (long)(stop_points(i) * (float)sample_frequency);
	  
	  // get the chopped data
	  //
	  chopped_audio_data = getChoppedData(audio_data,
					      start_sample,
					      stop_sample);
	  
	  // write to a file
	  //
	  file_name = writeChoppedData(chopped_audio_data,
				       output_format, input_format,
				       output_type, output_suffix,
				       output_extension,
				       input_file, output_directory,
				       output_preserve,
				       sample_frequency,
				       verbosity); 
	  
	  // if verbosity is greater than BRIEF output the
	  // endpoints
	  //
	  if (verbosity > Integral::BRIEF) {
	    String output;
	    output.concat(L"[");
	    output.concat((float)start_points(i));
	    output.concat(L", ");
	    output.concat((float)stop_points(i));
	    output.concat(L" ]  ");	  
	    output.concat(file_name);
	    Console::putNoWrap(output);
	  }
	  
	  // check whether we  have reached the last
	  // endpoint
	  // 
	  if ( i < (long)(start_points.length() - 1)) {  
	    
	    // get the start and the stop sample
	    //
	    start_sample = (long)(stop_points(i) * (float)sample_frequency);
	    stop_sample = (long)(start_points(i + 1) *
				 (float)sample_frequency);
	    
	    // get the chopped data
	    //
	    chopped_audio_data = getChoppedData(audio_data,
						start_sample,
						stop_sample);
	    
	    // write to a file
	    //
	    file_name = writeChoppedData(chopped_audio_data,
					 output_format, input_format,
					 output_type, output_suffix,
					 output_extension,
					 input_file, output_directory,
					 output_preserve,
					 sample_frequency,
					 verbosity);
	    
	    // if verbosity is greater than BRIEF output the
	    // endpoints
	    //
	    if (verbosity > Integral::BRIEF) {
	      String output;
	      output.concat(L"[");
	      output.concat((float)stop_points(i));
	      output.concat(L", ");
	      output.concat((float)start_points(i + 1));
	      output.concat(L" ]  ");	  
	      output.concat(file_name);
	      Console::putNoWrap(output);
	    }
	    
	  }    
	  
	}
	
	// get the start and the stop sample
	//
	start_sample = (long)(stop_points(start_points.length() - 1) *
			      (float)sample_frequency);
	stop_sample = audio_len;
		
	// get the chopped data
	//
	chopped_audio_data = getChoppedData(audio_data,
					    start_sample,
					    stop_sample);
	
	// write to a file
	//
	file_name = writeChoppedData(chopped_audio_data,
				     output_format, input_format,
				     output_type, output_suffix,
				     output_extension,
				     input_file, output_directory,
				     output_preserve,
				     sample_frequency,
				     verbosity);
	
	// if verbosity is greater than BRIEF output the
	// endpoints
	//
	if (verbosity > Integral::BRIEF) {
	  String output;
	  output.concat(L"[");
	  output.concat((float)stop_points(start_points.length() - 1));
	  output.concat(L", ");
	  output.concat(((float)audio_len/sample_frequency));
	  output.concat(L" ]  ");	  
	  output.concat(file_name);
	  Console::putNoWrap(output);
	}
	
      }
      
      //*****************************************************************
      // Browse up to find a detailed expalnation on how Mode 1 works
      // Mode 2: 
      // Example(Mode 2):
      // Lets consider we have an audio file - foo.wav
      // The Signal Detector finds three pairs of endpoints
      // Endpoints: t1-s1, t2-s2, t3-s3
      // The way the audio file is chopped for ALL mode is:
      // foo_000.wav conatining audio data from (t1 - s1)
      // foo_001.wav containing audio data from (t2 - s2)
      // foo_002.wav containing audio data from (t3 - s3)
      // So for three pairs of endpoints, we get 3 chopped files when
      // Mode is set to SIGNAL_ONLY mode. 
      // ****************************************************************
      
      else {
	
	// we get the number of endpoints from the length of the
	// startpoints vector. We loop that many times and write
	// enpointed wave files every loop
	//
	for(long i=0; i<start_points.length(); i++) {
	  
	  // get the start and the stop sample
	  //
	  long start_sample = (long)(start_points(i) *
				     (float)sample_frequency);
	  long stop_sample = (long)(stop_points(i) * (float)sample_frequency);
	  
	  // get the chopped data
	  //
	  chopped_audio_data = getChoppedData(audio_data,
					      start_sample,
					      stop_sample);
	  
	  // write to a file
	  //
	  Filename file_name = writeChoppedData(chopped_audio_data,
						output_format, input_format,
						output_type, output_suffix,
						output_extension,
						input_file, output_directory,
						output_preserve,
						sample_frequency,
						verbosity);
	  
	  // if verbosity is greater than BRIEF output the
	  // endpoints
	  //
	  if (verbosity > Integral::BRIEF) {
	    String output;
	    output.concat(L"[");
	    output.concat((float)start_points(i));
	    output.concat(L", ");
	    output.concat((float)stop_points(i));
	    output.concat(L" ]  ");	  
	    output.concat(file_name);
	    Console::putNoWrap(output);
	  }
	  
	}
	
      }
    }
    
    // increment the number of files successfully processed
    //
    num_processed++;
    
  } while (sdb.gotoNext());
  
   // close the log file
  //
  if (log_file.ne(CommandLine::DEF_LFILE_NAME)) {
    Console::close();
  }
  
  // exit gracefully
  //
  Integral::exit();
  
}

// method: getChoppedData
//
// arguments:
//  Vector<VectorFloat>&: the audio data
//  long start_sample: the start sample number
//  long stop_sample: the stop sample
//
// return: the chopped data (data between start and
// stop sample)
//
// this method takes as input the audio data and chops
// the audio samples between start and stop samples
//
Vector<VectorFloat> getChoppedData(Vector<VectorFloat> audio_data_a,
				   long start_sample_a,
				   long stop_sample_a) {
  
  // this array stores the chopped data
  //
  Vector<VectorFloat> chopped_audio_data;
  
  // set the length depending on the number of channels
  //
  chopped_audio_data.setLength(audio_data_a.length(), false);
  
  
  // the for loop loops around the samples between the endpoints 
  //
  for(long i = 0; i<audio_data_a.length(); i++) {
    
    // temp index
    //
    long index = 0;
    
    // set the length of the temporary buffer
    //
    chopped_audio_data(i).setLength((stop_sample_a - start_sample_a + 1),
				    false);
    
    for(long j = start_sample_a; j <= stop_sample_a; j++) {
      
      // storing samples in a temporary array
      //
      chopped_audio_data(i)(index) = audio_data_a(i)(j);
      
      // increment the array index
      //
      index++;
    }
  }
  
  // retrun the chopped audio data
  //
  return chopped_audio_data;
  
}

// method: writeChoppedData
//
// arguments:
//  Vector<VectorFloat>&: the chopped data
//  String file_format: set the file format 
//  String file_type: the file type
//  String input_format: set the input file format
//  String file_suffix: set suffix
//  String file_extension: set extension
//  Filename input_filename: set input filename
//  String output_dir: set output directory
//  long file_preserve: set preserve
//  long sample_frequency: set sample frequency
//  DebugLevel verbosity: set DebugLevel
//
// return: return filename of the written file  
//
// this method takes as input the audio data and chops
// the audio samples between start and stop samples
//
Filename writeChoppedData(Vector<VectorFloat>& chopped_data_a,
			  String file_format_a,
			  String input_format_a,
			  String file_type_a,
			  String file_suffix_a,
			  String file_extension_a,
			  Filename input_filename_a,
			  String output_dir_a,
			  long file_preserve_a,
			  long sample_frequency_a,
			  DebugLevel verbosity_a) {
  
  // create a temporary filename object
  // and assign it to the concatenated
  // string object
  //
  String filename;
  
  // get the filename in a string object
  //
  input_filename_a.getName(filename);
  
  // transform the input_filname using the
  // following parameters
  //
  input_filename_a.transform(output_dir_a,
			     file_extension_a,
			     file_suffix_a,
			     file_preserve_a);
  
  // create unique filenames
  //
  input_filename_a.transformUniquely();
  
  // build path for the output files
  //
  String temp_dir;
  input_filename_a.getDir(temp_dir);
  if (temp_dir.ne(CommandLine::DEF_EMPTY)) {
    Filename::buildPath(temp_dir);
  }
  
  // object for writing the wav file
  //
  AudioFile src_write;
  
  // check for wav format
  //
  if (file_format_a.eq(FORMAT_WAV)) {
    src_write.setFileFormat(AudioFile::WAV);  // set file format
  }
  
  // check for raw format
  //
  else if (file_format_a.eq(FORMAT_RAW)) {
    src_write.setFileFormat(AudioFile::RAW);  // set file format
    
  }
  
  // check for sof format
  //
  else if (file_format_a.eq(FORMAT_SOF)) {
    src_write.setFileFormat(AudioFile::SOF);  // set file format
    
  }
  
  // by default set the input file format
  //
  else {
    
    if (input_format_a.eq(FORMAT_WAV)) {
      src_write.setFileFormat(AudioFile::WAV);  // set file format
    }
    
    // check for raw format
    //
    else if (input_format_a.eq(FORMAT_RAW)) {
      src_write.setFileFormat(AudioFile::RAW);  // set file format
      
    }
    
    // check for sof format
    //
    else if (input_format_a.eq(FORMAT_SOF)) {
      src_write.setFileFormat(AudioFile::SOF);  // set file format
    }
  }
  
  // check if its is binary
  //
  if (file_type_a.eq(TYPE_BINARY)) {
    src_write.setFileType(AudioFile::BINARY);  // set file type
  }
  
  // else, set it to text
  //
  else {
    src_write.setFileType(AudioFile::TEXT);  // set file type
  }
  
  // TODO :
  // WAV is defined for LITTLE-ENDIAN
  //
  src_write.File::setBMode(File::LITTLE_ENDIAN);
  
  // set the number of channels
  //
  src_write.setNumChannels(chopped_data_a.length());
  
  // get the sample frequency from the param file
  // and set it.
  //
  src_write.setSampleFrequency(sample_frequency_a);
  
  // open a file for writing
  //
  src_write.open(input_filename_a, File::WRITE_ONLY);
  
  // write the endpointed samples to a file
  //
  src_write.writeAudioData(chopped_data_a,0);
  
  // close the output file
  //
  src_write.close();
  
  // return true
  //
  return input_filename_a;
  
}
