// file: $isip/class/sp/AudioFrontEnd/afnd_06.cc
// version: $Id: afnd_06.cc 10598 2006-08-08 21:45:21Z ss754 $
//

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

// method: processNextFrame
//
// arguments:
//
// return: a boolean value indicating status
//
// this method processes the next frame of data and append to circular buffer
//
boolean AudioFrontEnd::processNextFrame() {

  // check for end of data
  //
  if (!processFrame(buf_d.getLastIndex() + 1)) {
    
    // finish out the buffers that can approximate boundaries
    //
    finishFrames();
    
    return false;
  }
    
  if (buf_d.getBuffer()(0).getNumForward() > 0) {
    buf_d.advanceCurr();
  }
  else if (buf_d.getBuffer()(0).getNumElements() == 1) {
    buf_d.setFrameIndex(buf_d.getFrameIndex() + 1);
  }

  // advance the read pointer
  //
  buf_d.advanceRead();

  // exit gracefully
  //
  return true;
}

// method: processFrame
//
// arguments:
//  long frame_index: (input) frame to process
//  
// return: a boolean value indicating status
//
// this method processes the given frame of data
//
boolean AudioFrontEnd::processFrame(long frame_index_a) {
  
  if (coef_name_d.length() < 0) {
    return Error::handle(name(), L"processFrame",
			 ERR, __FILE__, __LINE__);
  }

  if (verbosity_d >= Integral::ALL) {
    String output(L"processing frame ");
    output.concat(frame_index_a);
    output.concat(L":\n");
    Console::put(output);
    Console::increaseIndention();
  }    

  // read input will pull the given frame out of the input and append
  // it to the end of the circular buffer
  //
  long new_offset = -1;
  long nchan = 1;

  if (!input_flag_d) { // this is pseudo input for no input file
    Vector<AlgorithmData> in_signal(nchan);
    for (long c = 0; c < nchan; c++) {
      in_signal(c).makeVectorFloat();    
      in_signal(c).getVectorFloat().setLength(5);
      for (long i = 0; i < 5; i++) {
	in_signal(c).getVectorFloat()(i) = 0;
      }
    }  
    buf_d.ensureChannels(SAMPLED_DATA_NAME, nchan);
    for (long i = 0; i < nchan; i++) {
      buf_d.getBuffer(SAMPLED_DATA_NAME)(i).append(in_signal(i));
    }
    new_offset = buf_d.getBuffer(SAMPLED_DATA_NAME)(0).getNumForward();
  }
  else if (!readInput(new_offset, frame_index_a)) {
    return false;
  }

  for (long i = 0;
       (i < coef_components_d.length()) && (i <= new_offset); i++) {

    // process these components with this offset
    //
    if (!processComponents(-i, new_offset)) {
      return false;
    }
  }

  buf_d.setLastIndex(buf_d.getLastIndex() + 1);
  
  if (verbosity_d >= Integral::ALL) {
    Console::decreaseIndention();
  }

  // exit gracefully
  //
  return true;
}

// method: finishFrames
//
// arguments: none
//  
// return: a boolean value indicating status
//
// this method finishes the processing of the given frame of data
//
boolean AudioFrontEnd::finishFrames() {
  
  // we now need to run the partial data through
  //
  long num_to_process = buf_d.getLastIndex() - buf_d.getFrameIndex();

  for (long i = 0; i < num_to_process; i++) {

    for (long j = i + 1; j < coef_components_d.length(); j++) {

      if (!processComponents(-j, num_to_process + i + 1, true)) {
	if (verbosity_d >= Integral::ALL) {
	  String output(L"Out of data in frame ");
	  output.concat(buf_d.getFrameIndex());
	  Console::put(output);
	}
      }
    }
  }
  
  // advance the pointers to the end
  //
  while (buf_d.getBuffer()(0).getNumForward() > 0) {
    buf_d.advanceCurr();
  }
  
  // exit gracefully
  //
  return true;
}

// method: readInput
//
// arguments:
//  long& new_offset: (output) how far forward
//  long frame_index: (input) frame to process
//  
// return: a boolean value indicating status
//
// this method reads the given frame of data
//
boolean AudioFrontEnd::readInput(long& new_offset_a, long frame_index_a) {

  // local variables
  //
  boolean no_data = true;
  long nchan = 1;
  Vector<AlgorithmData> in_signal;
  
  // initialize the output
  //
  new_offset_a = -1;

  if (debug_level_d >= Integral::ALL) {
    Long(frame_index_a).debug(L"frame_index_a");
  }
 
  // can we read audio data?
  //
  if (input_data_type_d == FrontEndBase::SAMPLED_DATA) {

    nchan = audio_input_d.getNumChannels();
    in_signal.setLength(nchan);
    for (long c = 0; c < nchan; c++) {
      in_signal(c).setCoefType(AlgorithmData::SIGNAL);
    }
    
    // we are adding data, set the flag
    //
    no_data = false;
    
    // read data from the AudioFile
    //
    long frame_nsamp = (long)Integral::round((double)frame_duration_d
				     * audio_input_d.getSampleFrequency());

    // partial reading start point
    //
    long start_point = (long)Integral::round((double)start_time_d
				     * audio_input_d.getSampleFrequency());

    // partial reading end point
    //
    long end_point = (long)Integral::round((double)end_time_d
				     * audio_input_d.getSampleFrequency());    
    
    long start_samp = (frame_index_a * frame_nsamp) + start_point;

    long one_frame = frame_nsamp;

    // partial reading last frame size
    //
    if ((start_samp + frame_nsamp > end_point) &&
	(end_time_d != DEF_END_TIME) &&
	(partial_process_d)) {
      one_frame =  end_point - start_samp; 
    }
    
    // check for end of data
    //
    long num_read = 0;
    for (long c = 0; c < nchan; c++) {
      if ((channel_index_d != DEF_CHANNEL_INDEX) &&
	  (c != (long)channel_index_d)) {
	if (debug_level_d >= Integral::ALL) {
	  String output(L"this channel is skiped: ");
	  output.concat(c);
	  output.concat(L"\n");
	  Console::put(output);
	  continue;
	}
      }
      num_read = audio_input_d.getData(in_signal(c).makeVectorFloat(), c,
				       start_samp, one_frame);
      
      buf_d.setLeftoverSamps(num_read);
    }

    if (num_read != frame_nsamp) {

      if (debug_level_d >= Integral::DETAILED) {
	String output(L"the file is out of data, last_samps = ");
	output.concat(num_read);
	output.concat(L", (");
	output.concat((double)num_read / (double)frame_nsamp);
	output.concat(L")\n");
	Console::put(output);
      }

      if (num_read == 0) {
	return false;
      }

      // pad the rest of the frame with zeros
      //
      for (long c = 0; c < nchan; c++) {
	if ((channel_index_d != DEF_CHANNEL_INDEX) &&
	    (c != (long)channel_index_d)) {
	  if (debug_level_d >= Integral::ALL) {
	    String output(L"this channel is skiped: ");
	    output.concat(c);
	    output.concat(L"\n");
	    Console::put(output);
	    continue;
	  }
	}
	in_signal(c).getVectorFloat().setLength(frame_nsamp);
	for (long i = num_read; i < frame_nsamp; i++) {
	  in_signal(c).getVectorFloat()(i) = 0;
	}
      }
    }

    if (debug_level_d >= Integral::ALL) {
      SAMPLED_DATA_NAME.debug(L"appending this coefficient");
      //      in_signal.debug(L"in_signal");
    }
    
    if (channel_index_d == DEF_CHANNEL_INDEX) {
      buf_d.ensureChannels(SAMPLED_DATA_NAME, nchan);
    }
    
    for (long i = 0; i < nchan; i++) {
      if ((channel_index_d != DEF_CHANNEL_INDEX) &&
	  (i != (long)channel_index_d)) {
	if (debug_level_d >= Integral::ALL) {
	  String output(L"this channel is skiped: ");
	  output.concat(i);
	  output.concat(L"\n");
	  Console::put(output);
	  continue;
	}
      }
      else if ((channel_index_d != DEF_CHANNEL_INDEX) &&
	       ( i == (long)channel_index_d)) {
	buf_d.getBuffer(SAMPLED_DATA_NAME)(0).append(in_signal(i));
      }

      if (channel_index_d == DEF_CHANNEL_INDEX) {
	buf_d.getBuffer(SAMPLED_DATA_NAME)(i).append(in_signal(i));
      }

    }
    
    new_offset_a = buf_d.getBuffer(SAMPLED_DATA_NAME)(0).getNumForward();

    double frame_percent = (double)num_read / (double)frame_nsamp;
    
    if (data_mode_d == AlgorithmBase::SAMPLE_BASED &&
	Integral::ceil(frame_percent) <= 0.0) {
      return false;
    }
    else if (data_mode_d == AlgorithmBase::FRAME_BASED &&
	     frame_percent < 1.0) {
      //      	     Integral::round(frame_percent) <= 0.0) {
      return false;
    }
    
  }

  // read from features file
  //
  else if (input_data_type_d == FrontEndBase::FEATURES) {
    
    nchan = feature_input_d.getNumChannels();
    in_signal.setLength(nchan);

    long index = (frame_index_a + offset_frame_d) * nchan;

    long num_read = 0;
    //MY
    //    for (long c = 0; c < nchan; c++) {
    //      in_signal(c).makeVectorFloat();
    //    }      
      
    num_read = feature_input_d.getBufferedData(in_signal, index, (long)nchan);

    long num_frames = getNumFrames();

    if (frame_index_a >= num_frames) {
      if (debug_level_d >= Integral::DETAILED) {
	String output(L"out of data from feature file, num_frames = ");
	output.concat(num_frames);
	Console::put(output);
      }
      return false;
    }
        
    if (debug_level_d >= Integral::DETAILED) {
      Long((long)Integral::round(num_frames)).debug(L"****after getData****");
    }

    buf_d.setLeftoverSamps(num_read);
    
    // we are adding data, set the flag
    //
    no_data = false;

    String vname(Recipe::INPUT_PREFIX);
    vname.concat(feature_input_d.getName());

    if (channel_index_d == DEF_CHANNEL_INDEX) {
      buf_d.ensureChannels(vname, nchan);
    }
    else {
      buf_d.ensureChannels(vname, 1);
    }
    
    for (long c = 0; c < nchan; c++) {
      //MY
      //      in_signal(c).setCoefType(feature_input_d.getCoefType());

      if ((channel_index_d != DEF_CHANNEL_INDEX) &&
	  (c != (long)channel_index_d)) {
	if (debug_level_d >= Integral::ALL) {
	  String output(L"this channel is skiped: ");
	  output.concat(c);
	  output.concat(L"\n");
	  Console::put(output);
	  continue;
	}
      }
      else if ((channel_index_d != DEF_CHANNEL_INDEX) &&
	       ( c == (long)channel_index_d)) {
	buf_d.getBuffer(vname)(0).append(in_signal(c));
      }
      
      if (channel_index_d == DEF_CHANNEL_INDEX) {
	
	buf_d.getBuffer(vname)(c).append(in_signal(c));
      }
    }

    if (debug_level_d >= Integral::ALL) {
      Long(frame_index_a).debug(L"frame_index_a");
      String output(L"appending coefficient <");
      output.concat(vname);
      output.concat(L", type = ");
      output.concat(AlgorithmData::CTYPE_MAP((long)feature_input_d.getCoefType()));
      output.concat(L">");
      in_signal.debug(L"in_signal");
      Console::put(output);
    }

    //MY
    //    buf_d.setLeftoverSamps(in_signal(0).getVectorFloat().length());
    
    long tmp = buf_d.getBuffer(vname)(0).getNumForward();
    if ((new_offset_a >= 0) && (new_offset_a != tmp)) {
      return Error::handle(name(), L"readInput", ERR, __FILE__, __LINE__);
    }
    new_offset_a = tmp;
  }

  else {
    debug(L"FrontEnd");
    return Error::handle(name(), L"readInput", Error::ARG,
			 __FILE__, __LINE__, Error::WARNING);    
  }
  
  // if there is no new data, we are processing from existing buffers
  //
  if (no_data) {

    // the negative starting value for buf_d.frame_index_d() causes
    // problems in the calculations, only subtract positive values to
    // obtain the proper number
    //
    if (buf_d.getFrameIndex() < 0) {
      new_offset_a = frame_index_a;
    }
    else {
      new_offset_a = frame_index_a - buf_d.getFrameIndex();
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: processComponents
//
// arguments:
//  long offset: (input) negative offset from current frame
//  long pad_offset: (input) positive offset from the current frame
//  boolean append: (input) should we append data to the end?
//  
// return: a boolean value indicating status
//
// this method processes the given frame of data
//
boolean AudioFrontEnd::processComponents(long offset_a,
					 long pad_offset_a, boolean append_a) {

  if ((offset_a > 0) || (-offset_a > coef_components_d.length())) {
    return Error::handle(name(), L"processComponents",
			 Error::ARG, __FILE__, __LINE__);
  }

  if (pad_offset_a < 0) {
    return Error::handle(name(), L"processComponents", Error::ARG,
			 __FILE__, __LINE__);
  }

  long num_recip = 0;
  long num_skipped = 0;
  boolean skip = false;

  // loop over all components
  //
  Component* proc_ptr;
  for (boolean more = coef_components_d(-offset_a).gotoFirst();
       more;
       more = coef_components_d(-offset_a).gotoNext()) {

    num_recip++;
    Vector<AlgorithmData> output;

    proc_ptr = coef_components_d(-offset_a).getCurr();
    if (proc_ptr == (Component*)NULL) {
      return Error::handle(name(), L"processComponents",
			   ERR, __FILE__, __LINE__);
    }

    long real_offset = offset_a + proc_ptr->getInputOffset(0) + pad_offset_a;
    
    if (real_offset > pad_offset_a) {
      return Error::handle(name(), L"processComponents",
			   ERR, __FILE__, __LINE__);
    }
    
    if (real_offset < 0) {
      return Error::handle(name(), L"processComponents",
			   ERR, __FILE__, __LINE__);
    }

    // two cases: either there is only one input or there are multiple
    // inputs. for a single input, we just use it directly. If there
    // are multiple inputs, we need to make an AlgorithmData object in
    // mode COMBINATION to hold a Vector of these inputs.
    //
    if (proc_ptr->getNumInputs() != 1) {

      // make sure we only need a single frame per input
      //
      if ((proc_ptr->getLeadingPad() != 0) || 
	  (proc_ptr->getTrailingPad() != 0)) {
	return Error::handle(name(), L"processComponents", Error::NOT_IMPLEM,
			     __FILE__, __LINE__);
      }

      // make sure the multiple inputs each have the same number of
      // channels
      //
      long num_inputs = proc_ptr->getNumInputs();

      long nchan = buf_d.getBuffer(proc_ptr->getInputName(0)).length();
      
      for (long i = 1; i < num_inputs; i++) {
	if (buf_d.getBuffer(proc_ptr->getInputName(i)).length() != nchan) {
	  return Error::handle(name(), L"processComponents",
			       ERR, __FILE__, __LINE__);
	}
      }

      // we need to produce a Vector<CircularBuffer<Vector<AlgoData>>
      //
      Vector< CircularBuffer<AlgorithmData> > tmp_buf(nchan);
      for (long c = 0; c < nchan; c++) {
	AlgorithmData obj;
	tmp_buf(c).append(obj);
	Vector<AlgorithmData>* bufs = &(tmp_buf(c)(0).makeCombination());
	bufs->setLength(num_inputs);

	for (long i = 0; i < num_inputs; i++) {

	  (*bufs)(i).swap(buf_d.getBuffer(proc_ptr->getInputName(i))(c)(real_offset));
	  //(*bufs)(i).assign(buf_d.getBuffer(proc_ptr->getInputName(i))(c)(real_offset));
	}
      }

      // call the Component set method
      //
      if (data_mode_d == AlgorithmBase::SAMPLE_BASED) {	
	proc_ptr->setLeftoverSamps(buf_d.getLeftoverSamps());
      }
      
      // call the Component apply method
      //
      proc_ptr->apply(output, tmp_buf);      

      // swap the data back out of temporary storage
      //
      for (long c = 0; c < nchan; c++) {
	Vector<AlgorithmData>* bufs = &(tmp_buf(c)(0).getCombination());
	for (long i = 0; i < num_inputs; i++) {
	  (*bufs)(i).swap(buf_d.getBuffer(proc_ptr->getInputName(i))(c)(real_offset));
	}
      }
      
      skip = false;
    }

    else {
      
      String in_name(proc_ptr->getInputName(0));
      
      // determine the number of channels in the input
      //
      long nchan = buf_d.getBuffer(in_name).length();
      
      long algo_trail_pad = (long)Integral::max(0, proc_ptr->getTrailingPad());
      long algo_lead_pad = (long)Integral::max(0, proc_ptr->getLeadingPad());

      if ((real_offset > buf_d.getBuffer(in_name)(0).getNumForward()) ||
	  (!append_a &&
	   (buf_d.getBuffer(in_name)(0).getNumForward() < 
	    (algo_lead_pad + real_offset)))) {
	num_skipped++;
	skip = true;
	if (debug_level_d >= Integral::ALL) {
	  proc_ptr->getOutputName().debug(L"skipping this coefficient");
	}
      } 
      else {
	skip = false;
      
	// seek forward to the offset time alignment
	//
	for (long i = 0; i < nchan; i++) {
	  buf_d.getBuffer(in_name)(i).seekCurr(real_offset);
	}
	
	// add the necessary pad before the samples
	//
	buf_d.ensureBufferTrailingPad(in_name, algo_trail_pad);
	
	// add the necessary pad after the samples, if append is set
	//
	if (append_a) {
	  buf_d.ensureBufferLeadingPad(in_name, algo_lead_pad);
	}
	
	// sanity check
	//
	if (buf_d.getBuffer(in_name)(0).getNumForward() < algo_lead_pad) {
	  return Error::handle(name(), L"processComponents",
			       ERR, __FILE__, __LINE__);
	}

	// call the Component set method
	//
	if (data_mode_d == AlgorithmBase::SAMPLE_BASED) {	
	  proc_ptr->setLeftoverSamps(buf_d.getLeftoverSamps());
	}
	
	// call the Component apply method
	//
	if (!proc_ptr->apply(output, buf_d.getBuffer(in_name))) {
	  return Error::handle(name(), L"processComponents",
			       ERR, __FILE__, __LINE__);
	}
		
	// seek back
	//
	for (long i = 0; i < nchan; i++) {
	  buf_d.getBuffer(in_name)(i).seekCurr(-real_offset);
	}
      }
    }

    if (!skip) {
      
      // now place the output onto the hashtable. note that we append
      // an empty object then swap the real data with the newly added
      // input. this is because a CircularBuffer only allows assigns
      // to its members.
      //
      long nchano = output.length();

      // make sure the buffer has enough channels
      //
      if (nchano > 1) {
	buf_d.ensureChannels(proc_ptr->getOutputName(), nchano);
      }
      
      Vector< CircularBuffer<AlgorithmData> >* ptr =
	&(buf_d.getBuffer(proc_ptr->getOutputName()));

      if (debug_level_d >= Integral::ALL) {
	proc_ptr->getOutputName().debug(L"appending this coefficient");
      }

      for (long i = 0; i < nchano; i++) {
	AlgorithmData obj;
	(*ptr)(i).append(obj);

	(*ptr)(i)((*ptr)(i).getNumForward()).swap(output(i));

	// sanity check
	//
	if (output(i).getDataType() != AlgorithmData::NONE) {
	  return Error::handle(name(), L"processComponents",
			       ERR, __FILE__, __LINE__);
	}
      }
    }
  }

  if ((num_recip > 0) && (num_skipped == num_recip)) {
    return false;
  }
  
  // exit gracefully
  //
  return true;
}

// method: open
//
// arguments:
//  const Filename& input: (input) input filename
//
// return: a boolean value indicating status
//
// this method opens the input files so we can begin processing
//
boolean AudioFrontEnd::open(const Filename& input_a) {

  if (input_flag_d) {
    
    if ( input_data_type_d == FrontEndBase::SAMPLED_DATA) {
      // this is an audio file
      //
      if (!audio_input_d.open(input_a)) {
	return Error::handle(name(), L"open", Error::ARG, __FILE__, __LINE__);
      }
      // get the sample frequency from audio input
      //
      sample_frequency_d = audio_input_d.getSampleFrequency();
    }
    
    else if (input_data_type_d == FrontEndBase::FEATURES) {
      // this is a features file
      //
      if (!feature_input_d.open(input_a)) {
	return Error::handle(name(), L"open", Error::ARG, __FILE__, __LINE__);
      }
      // get the sample frequency from feature input
      //
      sample_frequency_d.assign(feature_input_d.getSampleFrequency());
    }
    
    else {
      // not support type
      //
      return Error::handle(name(), L"open", Error::READ, __FILE__,
			   __LINE__);      
    }
    
  } // input flag if end

  input_filename_d.concat(input_a);
  
  buf_d.setCoefName(coef_name_d);

  // reset the buffers
  //
  resetBuffer();
  
  // now that we have all the input names set, compute the Component
  //
  processTarget();    

  if (debug_level_d >= Integral::BRIEF) {
    String str;
    String str_00;
    
    if (debug_level_d >= Integral::DETAILED) {
      str.assign(L"\n\n--------------------------------------------------------\n\n");
    }
    
    str.concat(L"processing ");

    for (long i = 0; i < input_filename_d.length() - 1; i++) {
      str_00.concat(input_filename_d(i));
      str_00.concat(L", ");
    }

    if (input_filename_d.length() > 0)
      str_00.concat(input_filename_d(input_filename_d.length() - 1));
    
    str.concat(str_00);    
    str.concat(L" -> ");

    str_00.assign(String::NULL_STRING);
    
    for (long i = 0; i < output_filename_d.length() - 1; i++) {
      str_00.concat(output_filename_d(i));
      str_00.concat(L", ");
    }

    if (output_filename_d.length() > 0)
      str_00.concat(output_filename_d(output_filename_d.length() - 1));
    
    str.concat(str_00);
    
    if (debug_level_d >= Integral::DETAILED) {
      str.concat(L"\n\n--------------------------------------------------------\n\n");
    }
    str.concat(L"\n");
    Console::putNoWrap(str);
  }
  
  if (debug_level_d >= Integral::DETAILED) {
    coef_components_d.debug(L"delayed components");
  }

  // to check if the file is empty or the data is too small for one
  // frame
  if (getNumFrames() < 1) {
    return Error::handle(name(), L"open", ERR_EMPTY, __FILE__, __LINE__);
  }
  
  // exit gracefully
  //
  return true;
}


// method: close
//
// arguments: none
//
// return: a boolean value indicating status
//
// this method closes the input files
//
boolean AudioFrontEnd::close() {

  if (audio_input_d.isOpen()) {
    audio_input_d.close();
  }
  
  if (feature_input_d.isOpen()) {
    feature_input_d.close();
  }

  resetBuffer();

  // exit gracefully
  //
  return true;
}

// method: run
//
// arguments:
//  const Filename& input: (input) input filename
//  
// return: a boolean value indicating status
//
// this method processes the given frame of data
//
boolean AudioFrontEnd::run(const Filename& input_a) {

  // get the file basename as the id
  //
  id_d.assign(input_a);
  
  // strip the extension
  //
  id_d.deleteRange(id_d.lastChr(L'.'), -1);

  input_filename_d.clear();
  output_filename_d.clear();
  
  // open the input file
  //
  if (!open(input_a)) {
    return Error::handle(name(), L"run", Error::ARG, __FILE__, __LINE__,
			 Error::WARNING);
  }

  // run through the data and output the features
  //
  outputFeatures();

  // close the input file
  //
  close();
  
  // exit gracefully
  //
  return true;
}

// method: outputFeatures
//
// arguments: none
//
// return: a boolean value indicating status
//
// this method runs through all the features and outputs to the given file
//
boolean AudioFrontEnd::outputFeatures() {

  // first we get the first frame worth of output so we know how many
  // channels there are
  //
  //  Vector<VectorFloat> data;
  // MY
  Vector<AlgorithmData> data;
  

  if (!getVector(data, 0)) {

    // ISIP_BUG #514: this needs to output an empty file
    //
    return Error::handle(name(), L"outputFeatures",
			 ERR, __FILE__, __LINE__);
  }

  long num_frames = getNumFrames();

  if (debug_level_d >= Integral::DETAILED) {
    Long((long)Integral::round(num_frames)).debug(L"****num_frames****");
  }
  
  // set the name of the features
  //
  if (coef_name_d.firstStr(Recipe::OUTPUT_PREFIX) != 0) {
    return Error::handle(name(), L"outputFeatures",
			 ERR, __FILE__, __LINE__);
  }
  
  String str;
  coef_name_d.substr(str, Recipe::OUTPUT_PREFIX.length());

  long samples_processed = 0;

  // get the number of inputs
  //
  long num_samples = 0;
  
  if (audio_input_d.isOpen()) {
    num_samples = audio_input_d.getNumSamples();
  }    
    
  for (long i = 0; i < num_frames; i++) {

    if (i > 0) {
      getVector(data, i);
    }
    
    // for the last frame we need to truncate so that the number of
    // samples in is the number of samples out
    //
    // This needs to check if the last algorithm
    // applied to the data was frame_based or sample_based, only do
    // the truncate if it was sample_based.
    //

    // get the samples processed up to this loop
    //
    // MY    
    //    samples_processed += data(0).length() * data.length();

    // this is code for generator class to output audio file
    //
    if (!input_flag_d) { 
      num_samples = samples_processed;
    }

    if ( i == num_frames - 1 && data_mode_d == AlgorithmBase::SAMPLE_BASED) {
      for (long ctag = 0; ctag < data.length(); ctag++) {
	// MY
	data(ctag).getVectorFloat().setLength(buf_d.getLeftoverSamps());
      }
    }

    //    if (data(0).getVectorFloat().length() == 0)
    //      continue;
  }
  
  // exit gracefully
  //
  return true;
}

// method: getVector
//
// arguments:
//  VectorFloat& data: (output) the audio data
//  long ctag: (input) the channel to read
//  long frame_index: (input) the requested frame
//  
// return: a boolean value indicating status
//
// this method gets the processed data. if the frame's data is already
// in the buffer, it is returned directly. if not the circular buffer
// is advanced until the frame is included.
//
// it returns false if the frame is out of the file's range
//
boolean AudioFrontEnd::getVector(VectorFloat& data_a,
				 long ctag_a, long frame_index_a) {

  long user_index = 0;
  
  // check argument
  //
  if (ctag_a < 0) {
    Error::handle(name(), L"getVector", Error::ARG, __FILE__, __LINE__);
    return -1;
  }

  // bad arguments
  //
  if (frame_index_a < 0) {
    Error::handle(name(), L"getVector", Error::ARG, __FILE__, __LINE__);
    return -1;
  }

  // possibly clear out the buffer if the new location is too far out of range
  //
  if (buf_d.contains(coef_name_d)) {

    long buf_max_frame = getLastFrame() +
      buf_d.getBuffer()(ctag_a).getCapacity();

    if ((frame_index_a < getFirstFrame()) || (frame_index_a > buf_max_frame)) {
    
      resetBuffer();

      // set new time index
      //
      buf_d.setFrameIndex(frame_index_a - 1);
    }
  }

  // process data until we have the needed frame
  //
  while (frame_index_a > getLastFrame()) {

    if (end_of_data_d) {
      data_a.clear();
      return false;
    }
    
    // this means we are out of data
    //
    if (!processNextFrame()) {
      end_of_data_d = true;
    }
  }
    
  // copy over the user's output 
  //
  user_index = frame_index_a - getLastFrame();

  if (buf_d.getData(ctag_a, user_index, coef_name_d).getDataType() != 
      AlgorithmData::VECTOR_FLOAT) {
    return Error::handle(name(), L"getVector",
			 ERR_NOVEC, __FILE__, __LINE__);
  }
  
  data_a.assign(buf_d.getData(ctag_a,
			      user_index, coef_name_d).getVectorFloat());

  if (debug_level_d >= Integral::DETAILED) {
    data_a.debug(L"AudioFrontEnd::getVector(0)");
  }

  // exit gracefully
  //
  return true;
}

//MY
// method: getVector
//
// arguments:
//  AlgorithmData& data: (output) the audio data
//  long ctag: (input) the channel to read
//  long frame_index: (input) the requested frame
//  
// return: a boolean value indicating status
//
// this method gets the processed data. if the frame's data is already
// in the buffer, it is returned directly. if not the circular buffer
// is advanced until the frame is included.
//
// it returns false if the frame is out of the file's range
//
boolean AudioFrontEnd::getVector(AlgorithmData& data_a,
				 long ctag_a, long frame_index_a) {

  long user_index = 0;
  
  // check argument
  //
  if (ctag_a < 0) {
    Error::handle(name(), L"getVector", Error::ARG, __FILE__, __LINE__);
    return -1;
  }

  // bad arguments
  //
  if (frame_index_a < 0) {
    Error::handle(name(), L"getVector", Error::ARG, __FILE__, __LINE__);
    return -1;
  }

  // possibly clear out the buffer if the new location is too far out of range
  //
  if (buf_d.contains(coef_name_d)) {

    long buf_max_frame = getLastFrame() +
      buf_d.getBuffer()(ctag_a).getCapacity();

    if ((frame_index_a < getFirstFrame()) || (frame_index_a > buf_max_frame)) {
    
      resetBuffer();

      // set new time index
      //
      buf_d.setFrameIndex(frame_index_a - 1);
    }
  }

  // process data until we have the needed frame
  //
  while (frame_index_a > getLastFrame()) {

    if (end_of_data_d) {
      data_a.clear();
      return false;
    }
    
    // this means we are out of data
    //
    if (!processNextFrame()) {
      end_of_data_d = true;
    }
  }
    
  // copy over the user's output 
  //
  user_index = frame_index_a - getLastFrame();

  /*  if (buf_d.getData(ctag_a, user_index, coef_name_d).getDataType() != 
      AlgorithmData::VECTOR_FLOAT) {
    return Error::handle(name(), L"getVector",
			 ERR_NOVEC, __FILE__, __LINE__);
			 }
  */
  
  data_a.assign(buf_d.getData(ctag_a,
			      user_index, coef_name_d));

  if (debug_level_d >= Integral::DETAILED) {
    data_a.debug(L"AudioFrontEnd::getVector(0)");
  }

  // exit gracefully
  //
  return true;
}

// method: processTarget
//
// arguments: none
//
// return: a boolean value indicating status
//
boolean AudioFrontEnd::processTarget() {

  if (recipe_d.isEmpty()) {
    
    if (component_list_d.length() == 0) {
      return Error::handle(name(), L"processTarget",
			   ERR, __FILE__, __LINE__);
    }
    Sof sof;
    sof.open(component_list_d);
    recipe_d.readGraph(sof, 0);
    sof.close();
  }

  SingleLinkedList<Component> components(DstrBase::USER);
  recipe_d.findComponent(components, *this, getCoefName());

  makeBuffers(components);
  preconfigureComponents(components);
  recipe_d.delayComponent(coef_components_d, buf_d, *this, components);
  configureComponents();

  // exit gracefully
  //
  return true;
}

// method: processTarget
//
// arguments:
//  Vector<String>& names: (output) target names
//  
// return: a boolean value indicating status
//
boolean AudioFrontEnd::processTarget(Vector<String>& names_a) {

  SingleLinkedList<Component> components(DstrBase::USER);
  recipe_d.findComponent(components, *this, names_a);
  makeBuffers(components);

  preconfigureComponents(components);
  recipe_d.delayComponent(coef_components_d, buf_d, *this, components);
  configureComponents();
    
  // exit gracefully
  //
  return true;  
}
