// file: $isip/class/mmedia/AudioFile/adf_12.cc
// version: $Id: adf_12.cc 9327 2003-10-27 17:19:32Z parihar $
//

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

// method: readAFData
//
// 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) number of samples
//  
// return: the number of samples read
//
// this method gets data from the audio file in a format supported by
// SGI's audiofile library and put each channel data into VectorFloat
//
long AudioFile::readAFData(Vector<VectorFloat>& data_a, long ctag_a,
			    long start_samp_a, long num_samp_a) {
  
#ifdef HAVE_AUDIOFILE

  // formats supported by SGI's audiofile library
  //
  if ((file_format_d != AIFF) && (file_format_d != AIFFC) &&
      (file_format_d != AU) && (file_format_d != BICSF) &&
      (file_format_d != WAV)) {
    Error::handle(name(), L"readAFData", 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"readAFData: 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 (start_samp_a != afSeekFrame(af_file_d, AF_DEFAULT_TRACK,
				  start_samp_a)) {
    return Error::handle(name(), L"readAFData", 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 (afReadFrames(af_file_d, AF_DEFAULT_TRACK, samples, num_read)
	!= num_read) {
      return Error::handle(name(), L"readAFData", 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;
    if (afReadFrames(af_file_d, AF_DEFAULT_TRACK, samples, num_read)
	!= num_read) {
          return Error::handle(name(), L"readAFData", Error::READ,
			       __FILE__, __LINE__, Error::WARNING);
    }

    interleaved_d.assign(num_read, samples);
  }
  else {
    Error::handle(name(), L"readAFData", 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 sgi's audiofile library is not available
  //
  Error::handle(name(), L"readAFData", Error::NOT_IMPLEM, __FILE__, __LINE__);
  return -1;
#endif
}

// method: writeAFData
//
// 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 audiofile in any of the
// formats supported by SGI's audiofile library
//
long AudioFile::writeAFData(Vector<VectorFloat>& data_a,
			       long ctag_a) {
  
#ifdef HAVE_AUDIOFILE

  // check for formats supported by SGI's audiofile library
  //
  if ((file_format_d != AIFF) && (file_format_d != AIFFC) &&
      (file_format_d != AU) && (file_format_d != BICSF) &&
      (file_format_d != WAV)) {
    return Error::handle(name(), L"writeAFData", ERR, __FILE__, __LINE__);
  }
  
  // not implemented
  //
  if ((long)num_channels_d != 1) {
    return Error::handle(name(), L"writeAFData", ERR, __FILE__, __LINE__);
  }
  
  if (sample_num_bytes_d == (Long)sizeof(byte8)) {
    byte8* samples = (byte8*)io_buf_d;
    for (long n = 0; n < data_a(0).length(); ++n) {
      *samples = (byte8)((float)(data_a(0)(n)) / max_sample_val_d);
      if (!afWriteFrames(af_file_d, AF_DEFAULT_TRACK, samples, 1)) {
	return Error::handle(name(), L"writeAFData", Error::IO,
			     __FILE__, __LINE__, Error::WARNING);
      }
    }    
  }
  
  else if (sample_num_bytes_d == (Long)sizeof(int16)) {
    int16* samples = (int16*)io_buf_d;
    for (long n = 0; n < data_a(0).length(); ++n) {
      *samples = (int16)((float)(data_a(0)(n)) / max_sample_val_d);
      if (!afWriteFrames(af_file_d, AF_DEFAULT_TRACK, samples, 1)) {
	return Error::handle(name(), L"writeAFData", Error::IO,
			     __FILE__, __LINE__, Error::WARNING);
      }      
    }
  }
  else {
    Error::handle(name(), L"writeAFData", Error::ENUM, __FILE__, __LINE__);
    return -1;
  }

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

// method: readAFHeader
//
// arguments: none
//  
// return: a boolean value indicating status
//
// this method reads the header of the audiofile in any of the formats
// supported by the SGI's audiofile library
//
boolean AudioFile::readAFHeader() {
  
#ifdef HAVE_AUDIOFILE
  
  // check for formats supported by the SGI's audiofile library
  //
  if ((file_format_d != AIFF) && (file_format_d != AIFFC) &&
      (file_format_d != AU) && (file_format_d != BICSF) &&
      (file_format_d != WAV)) {
    return Error::handle(name(), L"readAFHeader", ERR, __FILE__, __LINE__);
  }
  
  // make sure the object is allocated
  //
  if (af_file_d == (AFfilehandle)NULL) {
    return Error::handle(name(), L"readAFHeader", Error::MEM,
			 __FILE__, __LINE__);
  }
  
  // read the required parameters from the audiofile
  //
  num_channels_d = afGetChannels(af_file_d, AF_DEFAULT_TRACK);
  sample_freq_d = afGetRate(af_file_d, AF_DEFAULT_TRACK);
  sample_num_bytes_d = (long)afGetFrameSize(af_file_d, AF_DEFAULT_TRACK, 0);
  long byte_order = (long)afGetByteOrder(af_file_d, AF_DEFAULT_TRACK);

  // if the compression if employed, the audiofile library returns the
  // number of bytes per sample as zero, so set the number of bytes
  // per sample to the default(2)
  //
  if (sample_num_bytes_d == (long)0) {
    sample_num_bytes_d = DEF_SAMPLE_NUM_BYTES;
  }
  
  // now set the byte order
  //
  // little-endian
  // default for WAV
  //
  if (byte_order == AF_BYTEORDER_LITTLEENDIAN) {
    byte_mode_d = LITTLE_ENDIAN;
  }
  
  // big-endian
  // supported by only  AIFF/AIFF-C, NeXT(4), BICSF(4)
  //
  else if (byte_order == AF_BYTEORDER_BIGENDIAN) {
    byte_mode_d = BIG_ENDIAN;
  }
  
  // else error
  //
  else {
    return Error::handle(name(), L"readAFHeader", Error::ARG,
  			 __FILE__, __LINE__);
  }
  
  // exit gracefully
  //
  return true;
  
#else
  // error if sgi's audiofile library is not available
  //
  Error::handle(name(), L"readAFHeader", Error::NOT_IMPLEM, __FILE__,
		__LINE__);
  return -1;
#endif
}

// method: writeAFHeader
//
// arguments: none
//  
// return: a boolean value indicating status
//
// this method writes the header of the audiofile in any of the formats
// supported by the SGI's audiofile library
//
boolean AudioFile::writeAFHeader() const {
  
  // check for formats supported by the SGI's audiofile library
  //
  if ((file_format_d != AIFF) && (file_format_d != AIFFC) &&
      (file_format_d != AU) && (file_format_d != BICSF) &&
      (file_format_d != WAV)) {
    return Error::handle(name(), L"writeAFHeader", ERR, __FILE__, __LINE__);
  }
  
#ifdef HAVE_AUDIOFILE

  // file format type
  //
  // AIFF
  //
  if (file_format_d == AIFF) {
    afInitFileFormat(af_setup_d, AF_FILE_AIFF);
  }
  
  // AIFFC
  //
  else if (file_format_d == AIFFC) {
    afInitFileFormat(af_setup_d, AF_FILE_AIFFC);
  }
  
  // AU
  //
  else if (file_format_d == AU) {
    afInitFileFormat(af_setup_d, AF_FILE_NEXTSND);
  }

  // BICSF
  //
  else if (file_format_d == BICSF) {
    afInitFileFormat(af_setup_d, AF_FILE_BICSF);
  }
  
  // WAV
  //
  else if (file_format_d == WAV) {
    afInitFileFormat(af_setup_d, AF_FILE_WAVE);
  }
  
  // else error
  //
  else {
    return Error::handle(name(), L"writeAFHeader", Error::ARG,
  			 __FILE__, __LINE__);
  }
  
  // number of channels
  //
  afInitChannels(af_setup_d, AF_DEFAULT_TRACK, (int)num_channels_d);
  
  // sampling frequency
  //
  afInitRate(af_setup_d, AF_DEFAULT_TRACK, (double)sample_freq_d);
  
  // sample format and sample width should agree. Note that there is
  // one-to-one mapping for them. see http://btrzxa.fddi.uni-bayreuth.de/
  // cgi-bin/manpages/afInitSampleFormat/3dm for further details.
  //
  if (sample_num_bytes_d == (long)1) {
    afInitSampleFormat(af_setup_d, AF_DEFAULT_TRACK, AF_SAMPFMT_UNSIGNED,
		       sample_num_bytes_d*(long)8);
  }  
  else if (sample_num_bytes_d == (long)2) {
    afInitSampleFormat(af_setup_d, AF_DEFAULT_TRACK, AF_SAMPFMT_TWOSCOMP,
		       sample_num_bytes_d*(long)8);
  }  
  else if (sample_num_bytes_d == (long)4) {
    afInitSampleFormat(af_setup_d, AF_DEFAULT_TRACK, AF_SAMPFMT_FLOAT,
		       sample_num_bytes_d*(long)4);
  }  
  else if (sample_num_bytes_d == (long)8) {
    afInitSampleFormat(af_setup_d, AF_DEFAULT_TRACK, AF_SAMPFMT_DOUBLE,
		       sample_num_bytes_d*(long)8);
  }  
  else {
    return Error::handle(name(), L"writeAFHeader", Error::ARG,
  			 __FILE__, __LINE__);
  }
  
  // compression type, we don't use this functionality of the
  // audiofile. the compression type is set to the compression of
  // default file format type
  //

  // byte ordering
  //
  // little-endian
  // standard for WAV
  //
  if (byte_mode_d == LITTLE_ENDIAN) {
    afInitByteOrder(af_setup_d, AF_DEFAULT_TRACK, AF_BYTEORDER_LITTLEENDIAN);
  }
  
  // big-endian
  // supported by only  AIFF/AIFF-C, NeXT(4), BICSF(4)
  //
  else if (byte_mode_d == BIG_ENDIAN) {
    afInitByteOrder(af_setup_d, AF_DEFAULT_TRACK, AF_BYTEORDER_BIGENDIAN);
  }

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

  // exit gracefully
  //
  return true;

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