// file: $isip/class/mmedia/AudioFile/adf_13.cc
// version: $Id: adf_13.cc 9286 2003-08-05 19:46:21Z parihar $
//

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

// method: readSphereData
//
// arguments:
//  Vector<VectorFloat>& data: (output) the audio data
//  long ctag: (input) the channel to read
//  long start_samp: (input) the start sample of the audio data
//  long num_samp: (input) the number of samples to read
//  
// return: the number of samples read
//
// this method gets data from the sphere format audiofile and puts each
// channel data into a VectorFloat
//
long AudioFile::readSphereData(Vector<VectorFloat>& data_a,
			       long ctag_a, long start_samp_a,
			       long num_samp_a) {
  
#ifdef HAVE_SPHERE

  // format: SPHERE
  //
  if (file_format_d != SPHERE) {
    Error::handle(name(), L"readSphereData", ERR_TYPE, __FILE__, __LINE__);
    return -1;
  }
  
  // take care of the partial frame condition
  //
  long total_samples = getNumSamples() * num_channels_d;
  
  if ((num_samp_a == ALL_SAMPLES)
      || ((start_samp_a + num_samp_a) > total_samples)) {
    
    // set the number of samples to be all samples remaining
    //      
    num_samp_a = total_samples - start_samp_a;
    
    if (debug_level_d >= Integral::DETAILED) {
      String output;
      output.assign(num_samp_a);
      output.insert(L"readSphereData: clipping num_samp_a to ", 0);
      Console::put(output);
    }
  }

  data_a.setLength(num_channels_d);
  
  for (long i = 0; i < num_channels_d; i++) {
    data_a(i).setLength(num_samp_a / num_channels_d);
  }
  
  // seek to the right place
  //
  if (sp_seek(sp_file_d, start_samp_a, (long)0) > (long)0) {
    return Error::handle(name(), L"readSphereData", Error::IO,
			 __FILE__, __LINE__, Error::WARNING);
  }
  
  // temporary holder for all the data
  //
  long num_read = num_samp_a * num_channels_d;

  if (sample_num_bytes_d == (Long)sizeof(byte8)) {
    byte8* samples = (byte8*)io_buf_d;
    if (sp_read_data(samples, num_read, sp_file_d) != num_read) {
      return Error::handle(name(), L"readSphereData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }
    
    interleaved_d.assign(num_read, samples);
  }
  else if (sample_num_bytes_d == (Long)sizeof(int16)) {
    int16* samples = (int16*) io_buf_d;
    long temp = 0;
    temp = sp_read_data(samples, num_read, sp_file_d);
    if (temp != num_read) {
      return Error::handle(name(), L"readSphereData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }
    
    interleaved_d.assign(num_read, samples);
  }
  else {
    Error::handle(name(), L"readSphereData", Error::ENUM, __FILE__, __LINE__);
    return -1;
  }
  
  // now do interleave
  //
  for (long i = 0; i < num_channels_d; i++) {

    data_a(i).setLength(num_samp_a);

    for (long j = 0; j < num_samp_a; j++) {
      
      // pull the sample out of the single buffer
      //
      data_a(i)(j) = (float)interleaved_d(j * num_channels_d + i);
      
      // scale the sample
      //
      data_a(i)(j) /= max_sample_val_d;
    }
  }
  
  // exit gracefully
  //
  return num_samp_a;

#else
  // error if Nist's Sphere library is not available
  //
  Error::handle(name(), L"readSphereData", Error::NOT_IMPLEM, __FILE__,
  		__LINE__);
  return -1;
#endif
}

// method: writeSphereData
//
// arguments:
//  Vector<VectorFloat>& data: (input) the audio data to write
//  long ctag: (input) channel tag
//  
// return: the number of elements written
//
// this method writes the audio data to a sphere audio file
//
long AudioFile::writeSphereData(Vector<VectorFloat>& data_a, long ctag_a) {
  
#ifdef HAVE_SPHERE
  
  // format: SPHERE
  //
  if (file_format_d != SPHERE) {
    return Error::handle(name(), L"writeSphereData", ERR, __FILE__, __LINE__);
  }
  
  // combine the multi-channel data to a single Vector
  //
  VectorLong whole_data;
  
  revertData(whole_data, data_a, ctag_a);  
  
  long num_write = whole_data.length();
  
  if (sample_num_bytes_d == (Long)sizeof(byte8)) {

    byte8 buffer[num_write];
    
    for (long i = 0; i < num_write; i++) {
      buffer[i] = (byte8)whole_data(i);
    }

    // call the sphere library write method
    //
    if (sp_write_data(buffer, num_write, sp_file_d) != num_write) {
      return Error::handle(name(), L"writeSphereData", Error::WRITE,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }
  
  else if (sample_num_bytes_d == (Long)sizeof(int16)) {
    
    int16 buffer[num_write];
    
    for (long i = 0; i < num_write; i++) {
      buffer[i] = (int16)whole_data(i);
    }

    // call the sphere library write method
    //
    if (sp_write_data(buffer, num_write, sp_file_d) != num_write) {
      return Error::handle(name(), L"writeSphereData", Error::WRITE,
			   __FILE__, __LINE__, Error::WARNING);
    }    

  }
  
  else {
    Error::handle(name(), L"writeSphereData", Error::ENUM, __FILE__, __LINE__);
    return -1;
  }

  // exit gracefully
  //
  return true;
  
#else
  // error if Nist's Sphere library is not available
  //
  Error::handle(name(), L"writeSphereData", Error::NOT_IMPLEM, __FILE__,
		__LINE__);
  return -1;
#endif  
}

// method: readSphereHeader
//
// arguments: none
//  
// return: a boolean value indicating status
//
// this method read the sphere header
//
boolean AudioFile::readSphereHeader() {

#ifdef HAVE_SPHERE
  
  // format: SPHERE
  //
  if (file_format_d != SPHERE) {
    return Error::handle(name(), L"readSphereHeader", ERR, __FILE__, __LINE__);
  }
  
  // make sure the object is allocated
  //
  if (sp_file_d == (SP_FILE*)NULL) {
    return Error::handle(name(), L"readSphereHeader", Error::MEM,
			 __FILE__, __LINE__);
  }
  
  // read the parameters from the sphere header that are absolutely
  // needed to correctly read the samples
  //
  // number of channels
  //
  long num_channels = (long)1;
  if (sp_h_get_field(sp_file_d, "channel_count", T_INTEGER,
		     (void**)&num_channels)
      > (long)99) {
    return Error::handle(name(), L"readSphereHeader", Error::READ,
			 __FILE__, __LINE__);
  }
  if(!setNumChannels(num_channels)) {
    return Error::handle(name(), L"readSphereHeader", Error::READ,
			 __FILE__, __LINE__);
  }

  // sampling frequency
  //
  long sample_rate = (long)0;
  if (sp_h_get_field(sp_file_d, "sample_rate", T_INTEGER,
		     (void**)&sample_rate) > (long)99) {
    return Error::handle(name(), L"readSphereHeader", Error::READ,
			 __FILE__, __LINE__);
  }
  
  // number of bytes per sample
  //
  if (sp_h_get_field(sp_file_d, "sample_n_bytes", T_INTEGER,
		     (void**)&sample_num_bytes_d) > (long)99) {
    return Error::handle(name(), L"readSphereHeader", Error::READ,
			 __FILE__, __LINE__);
  }
  /*
  // read the parameters from the sphere header that are not necessary
  // to correctly read the samples. these parameters are only set to
  // return a right type if called from outside this class
  //
  // compression type
  //
  char* sample_coding = new char[99];
  if (sp_h_get_field(sp_file_d, "sample_coding", T_STRING,
  		     (void**)&sample_coding) > (long)99) {
    return Error::handle(name(), L"readSphereHeader", Error::READ,
  			 __FILE__, __LINE__);
  }
  
  // now set the compression type
  //
  // PCM
  //
  if (strcmp(sample_coding, "pcm") == (long)0) {
    compression_type_d = PCM;
  }

  // ALAW
  //
  else if (strcmp(sample_coding, "alaw") == (long)0) {
    //    compression_type_d = ALAW;
    return Error::handle(name(), L"readSphereHeader", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);    
  }

  // ULAW
  //
  else if (strcmp(sample_coding, "ulaw") == (long)0) {
    //    compression_type_d = ULAW;
    return Error::handle(name(), L"readSphereHeader", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);    
  }

  // PCM_SHORTEN1
  //
  else if (strcmp(sample_coding, "pcm,embedded-shorten-v1.0") == (long)0) {
    compression_type_d = PCM_SHORTEN1;
  }

  // PCM_SHORTEN2
  //
  else if (strcmp(sample_coding, "pcm,embedded-shorten-v2.00") == (long)0) {
    compression_type_d = PCM_SHORTEN2;
  }

  // ULAW_SHORTEN1
  //
  else if (strcmp(sample_coding, "ulaw,embedded-shorten-v1.0") == (long)0) {
    compression_type_d = ULAW_SHORTEN1;
  }

  // ULAW_SHORTEN2
  //
  else if (strcmp(sample_coding, "ulaw,embedded-shorten-v2.00") == (long)0) {
    compression_type_d = ULAW_SHORTEN2;
  }

  // PCM_WAVPACK1
  //
  else if (strcmp(sample_coding, "pcm,embedded-wavpack-v1.0") == (long)0) {
    compression_type_d = PCM_WAVPACK1;
  }

  // ULAW_WAVPACK1
  //
  else if (strcmp(sample_coding, "ulaw,embedded-wavpack-v1.0") == (long)0) {
    compression_type_d = ULAW_WAVPACK1;
  }

  // PCM_SHORTPACK0
  //
  else if (strcmp(sample_coding, "pcm,embedded-shortpack-v0") == (long)0) {
    compression_type_d = PCM_SHORTPACK0;
  }

  // else error
  //
  else {
    return Error::handle(name(), L"readSphereHeader", Error::READ,
  			 __FILE__, __LINE__);
  }

  // sample bytes format
  //
  char* sample_byte_format = new char[99];
  if (sp_h_get_field(sp_file_d, "sample_byte_format", T_STRING,
  		     (void**)&sample_byte_format) > (long)99) {
    //    return Error::handle(name(), L"readSphereHeader", Error::READ,
    //  			 __FILE__, __LINE__);
  }

  // now set the sample byte format
  //
  // little-endian
  //
  if (strcmp(sample_byte_format, "01") == (long)0) {
    byte_mode_d = LITTLE_ENDIAN;
  }

  // big-endian
  //
  else if (strcmp(sample_byte_format, "10") == (long)0) {
    byte_mode_d = BIG_ENDIAN;
  }

  // native
  //
  else if (strcmp(sample_byte_format, "1") == (long)0) {
    byte_mode_d = NATIVE;
  }

  // support the non-standard field as done by sphere. See
  // sphere-2.6a/doc/sphere.doc in the sphere documentation for
  // further details
  //
  else if (strcmp(sample_byte_format, "shortpack-v0") == (long)0) {
    compression_type_d = PCM_SHORTPACK0;
  }

  // else error
  //
  else {
    return Error::handle(name(), L"readSphereHeader", Error::READ,
  			 __FILE__, __LINE__);
  }

  // free the memory
  //
  if (sample_coding != (char*)NULL) {
    //    delete [] sample_coding;
    //    sample_coding = (char*)NULL;
  }
  if (sample_byte_format != (char*)NULL) {
    //    delete [] sample_byte_format;
    //    sample_byte_format = (char*)NULL;
  }
  */
  // exit gracefully
  //
  return true;
  
#else
  // error if Nist's Sphere library is not available
  //
  Error::handle(name(), L"readSphereHeader", Error::NOT_IMPLEM, __FILE__,
  		__LINE__);
  return -1;
#endif
}

// method: writeSphereHeader
//
// arguments: none
//  
// return: a boolean value indicating status
//
// this method writes the sphere header
//
boolean AudioFile::writeSphereHeader() const {

  // write to sphere file
  //
  if (file_format_d != SPHERE) {
    return Error::handle(name(), L"writeSphereHeader", ERR,
			 __FILE__, __LINE__);
  }
  
#ifdef HAVE_SPHERE
  
  // make sure the object is allocated
  //
  if (sp_file_d == (SP_FILE*)NULL) {
    return Error::handle(name(), L"writeSphereHeader", Error::MEM,
			 __FILE__, __LINE__);
  }
  
  // number of channels
  //
  if (sp_h_set_field(sp_file_d, "channel_count", T_INTEGER,
		     (void*)&num_channels_d)
      > (long)99) {
    return Error::handle(name(), L"writeSphereHeader", Error::WRITE,
			 __FILE__, __LINE__);
  }

  // sampling frequency
  //
  long sample_rate = sample_freq_d;
  if (sp_h_set_field(sp_file_d, "sample_rate", T_INTEGER,
		     (void*)&sample_rate) > (long)99) {
    return Error::handle(name(), L"writeSphereHeader", Error::WRITE,
			 __FILE__, __LINE__);
  }
  
  // number of bytes per sample
  //
  if (sp_h_set_field(sp_file_d, "sample_n_bytes", T_INTEGER,
		     (void*)&sample_num_bytes_d) > (long)99) {
    return Error::handle(name(), L"writeSphereHeader", Error::WRITE,
			 __FILE__, __LINE__);
  }

  // no need to set number of samples, it is set automatically while
  // writing the data to the sphere file
  //
  
  // compression type
  //
  char* sample_coding = new char[99];
  
  // PCM
  //
  if (compression_type_d == PCM) {
    sample_coding = "pcm";
  }
  
  // ALAW
  //
  else if (compression_type_d == ALAW) {
    //    sample_coding = "alaw";
    return Error::handle(name(), L"readSphereHeader", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);    
  }
  
  // ULAW
  //
  else if (compression_type_d == ULAW) {
    //    sample_coding = "ulaw";
    return Error::handle(name(), L"readSphereHeader", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);    
  }
  
  // PCM_SHORTEN1
  //
  else if (compression_type_d == PCM_SHORTEN1) {
    //    sample_coding = "pcm,embedded-shorten-v1.0";
    return Error::handle(name(), L"readSphereHeader", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);
  }
  
  // PCM_SHORTEN2
  //
  else if (compression_type_d == PCM_SHORTEN2) {
    //    sample_coding = "pcm,embedded-shorten-v2.00";
    return Error::handle(name(), L"readSphereHeader", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);    
  }
  
  // ULAW_SHORTEN1
  //
  else if (compression_type_d == ULAW_SHORTEN1) {
    //    sample_coding = "ulaw,embedded-shorten-v1.0";
    return Error::handle(name(), L"readSphereHeader", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);
  }
  
  // ULAW_SHORTEN2
  //
  else if (compression_type_d == ULAW_SHORTEN2) {
    //  sample_coding = "ulaw,embedded-shorten-v2.00";    
    return Error::handle(name(), L"readSphereHeader", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);
  }
  
  // PCM_WAVPACK1
  //
  else if (compression_type_d == PCM_WAVPACK1) {
    //    sample_coding = "pcm,embedded-wavpack-v1.0";
    return Error::handle(name(), L"readSphereHeader", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);
  }

  // ULAW_WAVPACK1
  //
  else if (compression_type_d == ULAW_WAVPACK1) {
    //    sample_coding = "ulaw,embedded-wavpack-v1.0";
    return Error::handle(name(), L"readSphereHeader", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);
  }
  
  // PCM_SHORTPACK0
  //
  else if (compression_type_d == PCM_SHORTPACK0) {
    //    sample_coding = "pcm,embedded-shortpack-v0";
    return Error::handle(name(), L"readSphereHeader", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);
  }

  // else error
  //
  else {
    return Error::handle(name(), L"writeSphereHeader", Error::NOT_IMPLEM,
  			 __FILE__, __LINE__);
  }
  
  // now set compression type
  //
  if (sp_h_set_field(sp_file_d, "sample_coding", T_STRING,
  		     (void*)sample_coding) > (long)99) {
    return Error::handle(name(), L"writeSphereHeader", Error::WRITE,
  			 __FILE__, __LINE__);
  }

  // sample bytes format
  //
  char* sample_byte_format = new char[99];

  // little-endian
  //
  if (byte_mode_d == LITTLE_ENDIAN) {
    sample_byte_format = "01";
  }

  // big-endian
  //
  else if (byte_mode_d == BIG_ENDIAN) {
    sample_byte_format = "10";
  }

  // native
  //
  else if (byte_mode_d == NATIVE) {
    sample_byte_format = "1";
  }

  // else error
  //
  else {
    return Error::handle(name(), L"writeSphereHeader", Error::WRITE,
  			 __FILE__, __LINE__);
  }

  // now set the sample byte format
  //
  sp_h_set_field(sp_file_d, "sample_byte_format", T_STRING,
		 (void*)sample_byte_format);  
  /*  if (sp_h_set_field(sp_file_d, "sample_byte_format", T_STRING,
      (void*)sample_byte_format) > (long)99) {
      return Error::handle(name(), L"writeSphereHeader", Error::WRITE,
      __FILE__, __LINE__);
      } */
  
  // free the memory
  //
  if (sample_coding != (char*)NULL) {
    //    delete [] sample_coding;
    //    sample_coding = (char*)NULL;
  }
  if (sample_byte_format != (char*)NULL) {
    //    delete [] sample_byte_format;
    //    sample_byte_format = (char*)NULL;
  }

  // exit gracefully
  //
  return true;
  
#else
  // not implemented
  //
  return Error::handle(name(), L"writeSphereHeader", Error::NOT_IMPLEM,
		       __FILE__, __LINE__);
#endif  
}
