// PyEPL: hardware/sound/soundFile.cpp
//
// Copyright (C) 2003-2005 Michael J. Kahana
// Authors: Ian Schleifer, Per Sederberg, Aaron Geller, Josh Jacobs
// URL: http://memory.psych.upenn.edu/programming/pyepl
//
// Distributed under the terms of the GNU Lesser General Public License
// (LGPL). See the license.txt that came with this file.
// soundFile class
//


#include "soundFile.h"

#include <iostream>
using std::cout;
using std::cerr;
using std::endl;


soundFile::soundFile(const char *filename, int mode, int format, int channels, int samplerate)
{
  file = NULL;

  // call the open method
  open(filename,mode,format,channels,samplerate);
  
}


soundFile::~soundFile()
{
  close();
}


int soundFile::open(const char *filename, int mode, int format, int channels, int samplerate)
{
  // see if should use provided info
  if (format != 0 )
  {
    info.format = format;
  }
  if (channels != 0)
  {
    info.channels = channels;
  }
  if (samplerate != 0)
  {
    info.samplerate = samplerate;
  }

  // open the file filling the info
  file = sf_open(filename, mode, &info);
  if (file == NULL)
  {
    cerr << "ERROR: sf_open failed." << endl;
    cerr << sf_strerror(file) << endl;
    return -1;
  }

  return 0;
}


int soundFile::close()
{
  // close the sndfile if it exists
  if (file)
  {
    return sf_close(file);
  }
  return 0;
}

long soundFile::getTotalSamples()
{
  return (long)(info.channels * info.frames);
}

int soundFile::getChannels()
{
  return info.channels;
}

int soundFile::getSamplerate()
{
  return info.samplerate;
}

int soundFile::getFormat()
{
  return info.format;
}

long soundFile::getFrames()
{
  return info.frames;
}


float *soundFile::readfile_float(int resampledrate)
{
  long toread = info.channels*info.frames;
  long numread;

  // allocate the space for the data
  float *data = new float[toread];

  // read in the data from the entire file
  numread = sf_readf_float(file,data,info.frames);
  if (numread != info.frames)
  {
    // Did not read in all that was expected
    cerr << "WARNING: sf_read only read " << numread << " out of " << info.frames << " frames." << endl;
    info.frames = numread;
  }

  // see if resample
  if (resampledrate > 0 && resampledrate != info.samplerate)
  {
    // resample the data to the new rate
    float *newdata = NULL;
    newdata = resample(data,(double)resampledrate/info.samplerate);
    
    // move the data around and delete the old data
    float *tempdata = data;
    data = newdata;
    newdata = tempdata;
    delete newdata;

    // set the new samplerate
    info.samplerate = resampledrate;
  }
  
  return data;
}

short *soundFile::readfile_short(int resampledrate)
{
  long toread;
  long numread;
  short *shortdata;

  // see if resample
  if (resampledrate > 0 && resampledrate != info.samplerate)
  {
    // we will resample, so should read in floats
    // get the data
    float *floatdata = NULL;
    floatdata = readfile_float(resampledrate);

    // convert the data to a float
    toread = info.channels*info.frames;
    shortdata = new short[toread];
  
    long i;
    for (i=0; i<toread; i++)
    {
      shortdata[i] = static_cast<short>(floatdata[i] * SHORT_RANGE);
    }

    // delete the float data
    delete floatdata;
  }
  else
  {
    // can just read short data
    toread = info.channels*info.frames;
    shortdata = new short[toread];

    // read in shorts from the entire file
    numread = sf_readf_short(file,shortdata,info.frames);
    if (numread != info.frames)
    {
      // Did not read in all that was expected
      cerr << "WARNING: sf_read only read " << numread << " out of " << info.frames << " frames." << endl;
      info.frames = numread;
    }
  }

  // return the short data
  return shortdata;
}

int soundFile::append_float(float *data, long numSamples)
{
  long ret;

  // File must be open

  // seek to the end
  ret = sf_seek(file, 0, SEEK_END);

  // append the float data
  ret = sf_write_float(file, data, numSamples);
  if (ret != numSamples)
  {
    // Did not write everything for some reason
    cerr << "WARNING: sf_write only appended " << ret << " out of " << numSamples << " samples." << endl;
  }
  return 0;
}

int soundFile::append_short(short *data, long numSamples)
{

  long ret;

  // File must be open

  // seek to the end
  ret = sf_seek(file, 0, SEEK_END);

  // append the float data
  ret = sf_write_short(file, data, numSamples);
  if (ret != numSamples)
  {
    // Did not write everything for some reason
    cerr << "WARNING: sf_write only appended " << ret << " out of " << numSamples << " samples." << endl;
  }
  return 0;
}


float *soundFile::resample(float *indata, double ratio)
{
  SRC_DATA sdata;

  // allocate space for new data
  long newsize = (long)(info.channels * ratio * info.frames);
  float *outdata = new float[newsize];
  
  // set up the struct
  sdata.data_in = indata;
  sdata.data_out = outdata;
  sdata.input_frames = info.frames;
  sdata.output_frames = (long)newsize/info.channels;
  sdata.src_ratio = ratio;

  int res = src_simple(&sdata, SRC_SINC_FASTEST, info.channels);

  if (res != 0)
  {
    cerr << "ERROR: Resampling failed." << endl;
    cerr << src_strerror(res) << endl;
    return NULL;
  }

  // update the frames to be the new number of samples
  info.frames = sdata.output_frames_gen;

  return outdata;
}
