/*
  PCM WAV Uncompressed I/O Library - Filter Functions
  T3 - Fontys Hogescholen
  Copyright 2006 Joris van Rooij

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation; either version 2
  of the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include <string.h>
#include <stdlib.h>
#include <math.h>
#include "pcm.h"

void filter_echo (char **out, struct wavheader *outfile, const char *data, const struct wavheader infile, const unsigned int delay, const unsigned int wet) {
  if (infile.bits_per_sample < 8)
    return;

  *outfile = infile;
  unsigned int bytes_per_sample = outfile->bits_per_sample/8;
  unsigned int delayed_samples = (outfile->sample_rate / 1000) * delay; /* FIXME: not exact */
  double lower = (double) wet / 100;
  double upper = (double) (100 - wet) / 100;
  outfile->data_size = infile.data_size + outfile->num_channels * bytes_per_sample * delayed_samples;
  *out = malloc(outfile->data_size);
  if (*out == NULL)
    return;
  
  memcpy(*out, data, infile.data_size);
  
  unsigned int i;
  short int buf1_16, buf2_16, buf3_16, buf3_8;
  unsigned char buf1_8, buf2_8, buf4_8;
  char *source, *target;
  for (i = bytes_per_sample * delayed_samples * outfile->num_channels; i < outfile->data_size; i += bytes_per_sample) {
    source = (char *) data + (i - bytes_per_sample * delayed_samples * outfile->num_channels);
    target = (char *) *out + i;
    switch (outfile->bits_per_sample) {
      case 8:
        memcpy(&buf1_8, source, bytes_per_sample);
        memcpy(&buf2_8, target, bytes_per_sample);
        buf3_8 = (buf1_8 - 128) * lower + (buf2_8 - 128) * upper;
        buf4_8 = buf3_8 + 128;
        memcpy(target, &buf4_8, bytes_per_sample);
      break;
      case 16:
        memcpy(&buf1_16, source, bytes_per_sample);
        memcpy(&buf2_16, target, bytes_per_sample);
        buf3_16 = buf1_16 * lower + buf2_16 * upper;
        memcpy(target, &buf3_16, bytes_per_sample);
      break;
      /* 24 bit (3B) and 32 bit (float) not yet supported */
      case 24:
      break;
      case 32:
      break;
    }
  }
}

void filter_backwards (char **out, struct wavheader *outfile, const char *data, const struct wavheader infile) {
  if (infile.bits_per_sample < 8)
    return;
  
  *outfile = infile;
  unsigned int bytes_per_multichannel_sample = (outfile->bits_per_sample / 8) * outfile->num_channels;
  char *source, *target;
  
  *out = malloc(outfile->data_size);
  if (*out == NULL)
    return;

  unsigned int i;
  for (i = outfile->data_size - bytes_per_multichannel_sample; i > 0; i -= bytes_per_multichannel_sample) {
    source = (char *) data + infile.data_size - bytes_per_multichannel_sample - i;
    target = (char *) *out + i;
    memcpy(target, source, bytes_per_multichannel_sample);
  }
}

void filter_stretch (char **out, struct wavheader *outfile, const char *data, const struct wavheader infile, const unsigned int stretch) {
  if (infile.bits_per_sample < 8)
    return;
  
  *outfile = infile;
  unsigned int bytes_per_multichannel_sample = (outfile->bits_per_sample / 8) * outfile->num_channels;
  char *source, *target;
  unsigned int num_samples_till_stretch, i, j = 0, stretch_samples = 0;
  
  if (stretch >= 100) {
    num_samples_till_stretch = (unsigned int) floor(100 / (stretch - 100));
    for (i = 0; i < infile.data_size; i += bytes_per_multichannel_sample) {
      if (j == num_samples_till_stretch) {
        outfile->data_size += bytes_per_multichannel_sample;
        j = 0;
      }
      outfile->data_size += bytes_per_multichannel_sample;
      j++;
    }
    
    *out = malloc(outfile->data_size);
    if (*out == NULL)
      return;

    j = 0;
    for (i = 0; i < infile.data_size; i += bytes_per_multichannel_sample) {
      source = (char *) data + i;
      target = (char *) *out + i + stretch_samples * bytes_per_multichannel_sample;
      if (j == num_samples_till_stretch) {
        memcpy(target, source, bytes_per_multichannel_sample);
        stretch_samples++;
        target = (char *) *out + i + stretch_samples * bytes_per_multichannel_sample;
        j = 0;
      }
      memcpy(target, source, bytes_per_multichannel_sample);
      j++;
    }
  } else {
    num_samples_till_stretch = (unsigned int) floor(100 / stretch);
    for (i = 0; i < infile.data_size; i += bytes_per_multichannel_sample) {
      if (j == num_samples_till_stretch) {
        outfile->data_size -= bytes_per_multichannel_sample;
        j = 0;
      }
      j++;
    }
    
    *out = malloc(outfile->data_size);
    if (*out == NULL)
      return;
    
    for (i = 0; i < infile.data_size; i += bytes_per_multichannel_sample) {
      if (j == num_samples_till_stretch) {
        stretch_samples++;
        j = 0;
      }
      source = (char *) data + i;
      target = (char *) *out + i - (stretch_samples * bytes_per_multichannel_sample);
      memcpy(target, source, bytes_per_multichannel_sample);
      j++;
    }
  }
}