// file: $isip/class/mmedia/AudioFile/adf_03.cc
// version: $Id: adf_03.cc 9266 2003-07-15 19:33:59Z parihar $
//

// isip include files
//
#include "AudioFile.h"
#include <Sof.h>
#include <VectorByte.h>
#include <VectorShort.h>
#include <VectorLong.h>

// method: read
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//  const String& name: (input) sof object instance name
//
// return: a boolean value indicating status
//
// this method has the object read itself from an Sof file
//
boolean AudioFile::read(Sof& sof_a, long tag_a, const String& name_a) {

  // get the instance of the object from the Sof file
  //
  if (!sof_a.find(name_a, tag_a)) {
    return false;
  }

  // read the actual data from the sof file
  //
  if (!readData(sof_a)) {
    return false;
  }

  // exit gracefully
  //
  return true;
}

// method: readData
//
// arguments:
//  Sof& sof: (input) sof file object
//  const String& pname: (input) parameter name
//  long size: (input) number of bytes in file
//  boolean param: (input) is the parameter specified?
//  boolean nested: (input) is this nested?
//
// return: a boolean value indicating status
//
// this method has the object read itself from an Sof file. it assumes
// that the Sof file is already positioned correctly.
//
boolean AudioFile::readData(Sof& sof_a, const String& pname_a, long size_a,
			    boolean param_a, boolean nested_a) {

  // if ascii, parse the start bracket
  //
  SofParser parser;
    
  if (nested_a) {
    parser.setNest();
  }

  // set the debug level
  //
  parser.setDebug(debug_level_d);
  
  // load the parse
  //
  parser.load(sof_a, size_a);

  if (nested_a) {
    parser.setNest();
  }

  // read the configuration parameters from the audiofile (sof
  // format)
  //
  if (!readSofConfig(sof_a, parser)) {
    return Error::handle(name(), L"readData", Error::READ, __FILE__,
			 __LINE__, Error::WARNING);
  }
  
  // query the data
  //
  no_data_d = true;
  if (parser.isPresent(sof_a, PARAM_DATA)) {
    // save the object size for later use
    //
    sof_length_pos_d = parser.getEntry(sof_a, PARAM_DATA);
    readSofData(sof_a);
    
    no_data_d = false;
  }
  else
    sampled_data_d.clear();
  
  // make sure all coefficients have been accessed
  //
  if (!parser.checkParams(sof_a)) {
    return Error::handle(name(), L"readData", Error::IO, __FILE__, __LINE__,
			 Error::WARNING);
  }
  
  // exit gracefully
  //
  return true;
}

// method: sofSize
//
// arguments: none
//
// return: the size of the class in bytes
//
// return the size of the class in bytes
//
long AudioFile::sofSize() const {

  // count up object by object
  //
  long s = 0;

  // for the two enums
  //
  s += FILE_TYPE_MAP.elementSofSize();
  s += FILE_FORMAT_MAP.elementSofSize();
  s += COMP_TYPE_MAP.elementSofSize();

  // rest of the class data
  //
  s += amplitude_range_d.sofSize();
  s += sample_freq_d.sofSize();
  s += sample_num_bytes_d.sofSize();
  s += SAMPLE_PRECISION_MAP.elementSofSize();

  s += BMODE_MAP.elementSofSize();
  
  s += num_channels_d.sofSize();
  s += tag_d.sofSize();
  s += block_size_d.sofSize();
  s += buf_size_d.sofSize();

  s += id_d.sofSize();
  
  s += ma_coeff_d.sofSize();
  s += ar_coeff_d.sofSize();

  // return the size
  //
  return s;
}

// method: readSofStart
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//  const String& name: (input) sof object instance name
//
// return: a boolean value indicating status
//
// this method has the object read itself from an Sof file
//
boolean AudioFile::readSofStart(Sof& sof_a, long tag_a, const String& name_a) {
  
  // get the instance of the object from the Sof file
  //
  if (!sof_a.find(name_a, tag_a)) {
    return false;
  }

  // read the actual data from the sof file
  //
  if (!readSofStartData(sof_a)) {
    return false;
  }

  // exit gracefully
  //
  return true;
}

// method: readSofStartData
//
// arguments:
//  Sof& sof: (input) sof file object
//  const String& pname: (input) parameter name
//  long size: (input) size of the object
//  boolean param: (input) is the parameter specified?
//  boolean nested: (input) is this nested?
//
// return: a boolean value indicating status
//
// this method allocate an SofParser object and load the parse. it is
// used for partial read.
//
boolean AudioFile::readSofStartData(Sof& sof_a, 
				    const String& pname_a,
				    long size_a, boolean param_a,
				    boolean nested_a) {
  String empty_str;
  
  SofParser parser;
  parser.setDebug(debug_level_d);

  // ignore implicit parameter setting
  //
  
  // are we nested?
  //
  if (nested_a) {
    parser.setNest();
  }
  
  // load the parse
  //
  if (!parser.load(sof_a, size_a)) {
    return Error::handle(name(), L"readSofStartData", Error::READ,
			 __FILE__, __LINE__, Error::WARNING);
  }

  // read the configuration parameters from audiofile (sof format)
  //
  if (!readSofConfig(sof_a, parser)) {
    return Error::handle(name(), L"readSofStartData", Error::READ, __FILE__,
			 __LINE__, Error::WARNING);
  }

  // query the data
  //
  no_data_d = true;
  if (parser.isPresent(sof_a, PARAM_DATA)) {

    // save the object size for later use
    //
    sof_length_pos_d = parser.getEntry(sof_a, PARAM_DATA);
    no_data_d = false;
  }
  
  // make sure all coefficients have been accessed
  //
  if (!parser.checkParams(sof_a)) {
    return Error::handle(name(), L"readSofStartData", Error::IO, __FILE__,
			 __LINE__, Error::WARNING);
  }

  // exit gracefully
  //
  return true;
}

// method: readSofConfig
//
// arguments:
//  Sof& sof: (input) sof file object
//  SofParser& parser: (output) parser object
//
// return: a boolean value indicating status
//
// this method reads the configuration parameters from the Sof object
// using the parser object. it assumes the parse has already been
// loaded before calling. this method exists since both the readData
// and readStart methods need to read in these configuration
// parameters so it prevents the duplication of code.
//
boolean AudioFile::readSofConfig(Sof& sof_a, SofParser& parser_a) {
  
  // read the data
  //
  FILE_TYPE ftype;
  if (parser_a.isPresent(sof_a, PARAM_FILE_TYPE)) {
    if (!FILE_TYPE_MAP.readElementData((long&)ftype, sof_a,
				       PARAM_FILE_TYPE,
				       parser_a.getEntry(sof_a,
						       PARAM_FILE_TYPE))) {
      return Error::handle(name(), L"readSofConfig", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }
  }
  
  else {
    ftype = DEF_FILE_TYPE;
  }
  
  FILE_FORMAT fformat;
  if (parser_a.isPresent(sof_a, PARAM_FILE_FORMAT)) {
    if (!FILE_FORMAT_MAP.readElementData((long&)fformat, sof_a,
					 PARAM_FILE_FORMAT,
					 parser_a.getEntry(sof_a,
							 PARAM_FILE_FORMAT))) {
      return Error::handle(name(), L"readSofConfig", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }
  }
  
  else {
    fformat = DEF_FILE_FORMAT;
  }

  // now we can set the file type
  //
  setFileType(ftype);
  setFileFormat(fformat);

  COMP_TYPE ctype;
  if (parser_a.isPresent(sof_a, PARAM_COMP_TYPE)) {
    if (!COMP_TYPE_MAP.readElementData((long&)ctype, sof_a,
				       PARAM_COMP_TYPE,
			       parser_a.getEntry(sof_a, PARAM_COMP_TYPE))) {
      return Error::handle(name(), L"readSofConfig", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }
  }
  else {
    ctype = DEF_COMP_TYPE;
  }
  setCompType(ctype);
  
  if (parser_a.isPresent(sof_a, PARAM_RANGE)) {
    if (!amplitude_range_d.readData(sof_a, PARAM_RANGE,
				    parser_a.getEntry(sof_a, PARAM_RANGE))) {
      return Error::handle(name(), L"readSofConfig", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }					   
  }
  else {
    amplitude_range_d.assign(DEF_AMPLITUDE_RANGE);
  }

  if (parser_a.isPresent(sof_a, PARAM_SAMPLE_FREQUENCY)) {
    if (!sample_freq_d.readData(sof_a, PARAM_SAMPLE_FREQUENCY,
				parser_a.getEntry(sof_a,
						PARAM_SAMPLE_FREQUENCY))) {
      return Error::handle(name(), L"readSofConfig", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }	      
  }
  else {
    sample_freq_d.assign(DEF_SAMPLE_FREQ);
  }

  Long num;
  if (parser_a.isPresent(sof_a, PARAM_SAMPLE_NUM_BYTES)) {
    if (!num.readData(sof_a, PARAM_SAMPLE_NUM_BYTES,
		      parser_a.getEntry(sof_a, PARAM_SAMPLE_NUM_BYTES))) {
      return Error::handle(name(), L"readSofConfig", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }	
  }
  else {
    num.assign(DEF_SAMPLE_NUM_BYTES);
  }
  setSampleNumBytes(num);

  SAMPLE_PRECISION samp_prec;
  if (parser_a.isPresent(sof_a, PARAM_SAMPLE_PRECISION)) {
    if (!SAMPLE_PRECISION_MAP.readElementData((long&)samp_prec, sof_a,
					      PARAM_SAMPLE_PRECISION,
					      parser_a.getEntry(sof_a,
							      PARAM_SAMPLE_PRECISION))) {
      return Error::handle(name(), L"readSofConfig", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }
  }
  
  else {
    samp_prec = DEF_SAMPLE_PRECISION;
  }
  setSamplePrecision(samp_prec);

  BMODE bmode = DEF_BMODE;
  if (parser_a.isPresent(sof_a, PARAM_BYTE_ORDER)) {
    if (!BMODE_MAP.readElementData((long&)bmode, sof_a, 
				   PARAM_BYTE_ORDER,
				   parser_a.getEntry(sof_a, PARAM_BYTE_ORDER))) {
      return Error::handle(name(), L"readSofConfig", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }
  }
  else {
    bmode = DEF_BMODE;
  }
  setBMode(bmode);

  Long num_chan;
  if (parser_a.isPresent(sof_a, PARAM_NUM_CHANNELS)) {
    if (!num_chan.readData(sof_a, PARAM_NUM_CHANNELS,
			   parser_a.getEntry(sof_a, PARAM_NUM_CHANNELS))) {
      return Error::handle(name(), L"readSofConfig", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }	      
  }
  else {
    num_chan.assign(DEF_NUM_CHANNELS);
  }

  // set the number of channels
  //
  setNumChannels(num_chan);
  
  if (parser_a.isPresent(sof_a, PARAM_TAG)) {
    if (!tag_d.readData(sof_a, PARAM_TAG, parser_a.getEntry(sof_a, PARAM_TAG))) {
      return Error::handle(name(), L"readSofConfig", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }	      
  }
  else {
    tag_d.assign(DEF_TAG);
  }

  if (parser_a.isPresent(sof_a, PARAM_BLOCK_SIZE)) {
    if (!block_size_d.readData(sof_a, PARAM_BLOCK_SIZE,
			       parser_a.getEntry(sof_a, PARAM_BLOCK_SIZE))) {
      return Error::handle(name(), L"readSofConfig", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }	      
  }
  else {
    block_size_d.assign(DEF_BLOCK_SIZE);
  }

  if (parser_a.isPresent(sof_a, PARAM_BUF_SIZE)) {
    if (!buf_size_d.readData(sof_a, PARAM_BUF_SIZE,
			     parser_a.getEntry(sof_a, PARAM_BUF_SIZE))) {
      return Error::handle(name(), L"readSofConfig", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }	      
  }
  else {
    buf_size_d.assign(DEF_BUF_SIZE);
  }

  if (parser_a.isPresent(sof_a, PARAM_ID)) {
    if (!id_d.readData(sof_a, PARAM_ID,
		       parser_a.getEntry(sof_a, PARAM_ID))) {
      return Error::handle(name(), L"readSofConfig", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }	      
  }
  else {
    id_d.clear();
  }

  // for the filter coefficients we must go through the set methods to
  // resize the buffers accordingly
  //
  VectorFloat ma_coeff;

  if (parser_a.isPresent(sof_a, PARAM_MA_COEFF)) {
    if (!ma_coeff.readData(sof_a, PARAM_MA_COEFF,
			   parser_a.getEntry(sof_a, PARAM_MA_COEFF),
			   false, false)) {
      return Error::handle(name(), L"readSofConfig", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }	      
  }
  else {
    ma_coeff.assign(DEF_MA_COEFF);
  }

  setMaCoeff(ma_coeff);

  VectorFloat ar_coeff;
  
  if (parser_a.isPresent(sof_a, PARAM_AR_COEFF)) {
    if (!ar_coeff.readData(sof_a, PARAM_AR_COEFF,
			   parser_a.getEntry(sof_a, PARAM_AR_COEFF),
			   false, false)) {
      return Error::handle(name(), L"readSofConfig", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }	      
  }
  else {
    ar_coeff.assign(DEF_AR_COEFF);
  }

  setArCoeff(ar_coeff);
  
  // exit gracefully
  //
  return true;
}

// method: readSofData
//
// arguments:
//  Sof& sof: (input) sof file object
//  
// return: number of samples read
//
// this method gets data from the sof audio file and put each channel data
// into a VectorFloat
//
long AudioFile::readSofData(Sof& sof_a) {
  
  // local variable for number of samples
  //
  long num_samp_a = 0;
  
  // set the length for channels
  //
  sampled_data_d.setLength(num_channels_d);

  // branch on number of bytes per sample
  //

  // 8 bit samples, read single bytes
  //   
  if (sample_num_bytes_d == (Long)sizeof(byte8)) {

    VectorByte vec;
    
    long num_read;
    vec.readData(sof_a, PARAM_DATA, SofParser::FULL_OBJECT,
				 true, false);
    num_read = vec.length();
    
    num_samp_a = num_read / num_channels_d;
    
    for (long i = 0; i < num_channels_d; i++) {
      sampled_data_d(i).setLength(num_samp_a);
      for (long j = 0; j < num_samp_a; j++) {
	sampled_data_d(i)(j) = (float)vec(j * num_channels_d + i) / max_sample_val_d;
      }
    }
  }
  
  // 16 bit samples, read short integers
  //   
  else if (sample_num_bytes_d == (Long)sizeof(int16)) {

    VectorShort vec;

    long num_read;
    vec.readData(sof_a, PARAM_DATA, SofParser::FULL_OBJECT, true, false);

    num_read = vec.length();
    num_samp_a = num_read / num_channels_d;
    
    for (long i = 0; i < num_channels_d; i++) {
      sampled_data_d(i).setLength(num_samp_a);
      for (long j = 0; j < num_samp_a; j++) {
	sampled_data_d(i)(j) = (float)vec(j * num_channels_d + i) / max_sample_val_d;
      }
    }
  }

  // 32 bit samples, read long integers
  //   
  else if (sample_num_bytes_d == (Long)sizeof(int32)) {

    VectorLong vec;

    long num_read;
    vec.readData(sof_a, PARAM_DATA, SofParser::FULL_OBJECT, true, false);

    num_read = vec.length();    
    num_samp_a = num_read / num_channels_d;
    
    for (long i = 0; i < num_channels_d; i++) {
      sampled_data_d(i).setLength(num_samp_a);
      for (long j = 0; j < num_samp_a; j++) {
	sampled_data_d(i)(j) = (float)vec(j * num_channels_d + i) / max_sample_val_d;
      }
    }
  }
  
  // return the number of samples read
  //
  return num_samp_a;
}
