#include <stdio.h>
#include <stdlib.h>

#include "fmt_chunk.h"

#define DEPTH 11000

int depth;
float decay;

int read_chars(FILE *in, char *s, int n)
{
int t,ch;

  for (t=0; t<n; t++)
  {
    ch=getc(in);
    if (ch==EOF) return -1;
    s[t]=ch;
  }
  s[t]=0;

  return 0;
}

int write_long(FILE *out, int n)
{

  putc((n&255),out);
  putc(((n>>8)&255),out);
  putc(((n>>16)&255),out);
  putc(((n>>24)&255),out);

  return 0;
}

int write_word(FILE *out, int n)
{

  putc((n&255),out);
  putc(((n>>8)&255),out);

  return 0;
}

unsigned int read_long(FILE *in)
{
unsigned int t;

  t=getc(in);
  t=(getc(in)<<8)+t;
  t=(getc(in)<<16)+t;
  t=(getc(in)<<24)+t;

  return t;
}

unsigned short int read_word(FILE *in)
{
unsigned short int t;

  t=getc(in);
  t=(getc(in)<<8)+t;

  return t;
}

int parse_header(FILE *in, FILE *out)
{
int length;
char riff_type[5];

  length=read_long(in);
  read_chars(in,riff_type,4);

  printf("RIFF Header\n");
  printf("----------------------------\n");
  printf("          Length: %d\n",length);
  printf("            Type: %s\n",riff_type);
  printf("----------------------------\n");

  /* Write RIFF Header */

  if (out!=0)
  {
    fprintf(out,"RIFF");
    write_long(out,4);
    fprintf(out,"WAVE");
  }

  return 0;
}

int parse_fmt(FILE *in, struct fmt_chunk_t *fmt_chunk, FILE *out)
{
int length;

  length=read_long(in);
  fmt_chunk->format_type=read_word(in);
  fmt_chunk->channel_numbers=read_word(in);
  fmt_chunk->sample_rate=read_long(in);
  fmt_chunk->bytes_per_second=read_long(in);
  fmt_chunk->bytes_per_sample=read_word(in);
  fmt_chunk->bits_per_sample=read_word(in);

  printf("FMT Chunk\n");
  printf("----------------------------\n");
  printf("          Length: %d\n",length);
  printf("     Format Type: ");
  if (fmt_chunk->format_type==0)
  { printf("Mono\n"); }
    else
  if (fmt_chunk->format_type==1)
  { printf("Stereo\n"); }
    else
  { printf("unknown\n"); }

  printf(" Channel Numbers: %d\n",fmt_chunk->channel_numbers);
  printf("     Sample Rate: %d\n",fmt_chunk->sample_rate);
  printf("Bytes Per Second: %d\n",fmt_chunk->bytes_per_second);
  printf("Bytes Per Sample: ");
  if (fmt_chunk->bytes_per_sample==1)
  { printf("8 bit mono (%d)\n",fmt_chunk->bytes_per_sample); }
    else
  if (fmt_chunk->bytes_per_sample==2)
  { printf("8 bit stereo or 16 bit mono (%d)\n",fmt_chunk->bytes_per_sample); }
    else
  if (fmt_chunk->bytes_per_sample==4)
  { printf("16 bit stereo (%d)\n",fmt_chunk->bytes_per_sample); }

  printf(" Bits Per Sample: %d\n",fmt_chunk->bits_per_sample);
  printf("----------------------------\n");

  /* Write FMT Chunk */

  if (out!=0)
  {
    fprintf(out,"fmt ");
    write_long(out,16);
    write_word(out,fmt_chunk->format_type);
    write_word(out,fmt_chunk->channel_numbers);
    write_long(out,fmt_chunk->sample_rate);
    write_long(out,fmt_chunk->bytes_per_second);
    write_word(out,fmt_chunk->bytes_per_sample);
    write_word(out,fmt_chunk->bits_per_sample);
  }

  return 0;
}

int parse_data(FILE *in, struct fmt_chunk_t *fmt_chunk, FILE *out)
{
int length,deepest;
short int reverb_buffer[depth];
int t,ptr;
char h,oldh;
short int r,oldr;

  deepest=0;

  length=read_long(in);

  for (t=0; t<depth; t++)
  {
    reverb_buffer[t]=0;
  }

  printf("DATA chunk\n");
  printf("----------------------------\n");
  printf("          Length: %d\n",length);

  /* Write Data Chunk */

  fprintf(out,"data");
  write_long(out,length+depth*3);
  ptr=0;

  if (fmt_chunk->bits_per_sample==16)
  {
    for (t=0; t<length/2; t++)
    {
      r=getc(in)+(getc(in)<<8);
      oldr=r;
      r=((r)+reverb_buffer[ptr])/2;
      reverb_buffer[ptr]=(int)((float)reverb_buffer[ptr]*decay);
      reverb_buffer[ptr]=((reverb_buffer[ptr])+oldr)/2;
      write_word(out,r);
      ptr++;
      if (ptr>=depth) ptr=0;
    }

    for (t=0; t<depth*3; t++)
    {
      write_word(out,reverb_buffer[ptr]);
      reverb_buffer[ptr]=reverb_buffer[ptr]>>1;
      ptr++;
      if (ptr>=depth) ptr=0;
    }
  }
    else
  if (fmt_chunk->bits_per_sample==8)
  {
    printf("This program only works with 16 bit samples.\n");
    return 1;
  }

  return 0;
}

int reverb(char *inname,char *outname)
{
FILE *in,*out;
char chunk_name[5];
struct fmt_chunk_t fmt_chunk;
int t,marker;

  in=fopen(inname,"rb");
  if (in==0)
  {
    printf("Couldn't open file for reading: %s\n",inname);
    return -1;
  }

  if (outname[0]!=0)
  {
    out=fopen(outname,"wb");
    if (out==0)
    {
      printf("Could not open file for writing: %s\n",outname);
      return 0;
    }
  }
    else
  { out=0; }


  while(1)
  {
    if (read_chars(in,chunk_name,4)!=0) break;

    if (strcasecmp(chunk_name,"RIFF")==0)
    { parse_header(in,out); }
      else
    if (strcasecmp(chunk_name,"fmt ")==0)
    { parse_fmt(in,&fmt_chunk,out); }
      else
    if (strcasecmp(chunk_name,"data")==0)
    { parse_data(in,&fmt_chunk,out); }
      else
    {
      printf("Unknown chunk: '%s'\n",chunk_name);
      return -1;
    }
  }

  marker=ftell(out);
  fseek(out,4,SEEK_SET);
  write_word(out,marker);
  fseek(out,marker,SEEK_SET);

  if (out!=0) fclose(out);
  fclose(in);

  return 0;
}

int main(int argc, char *argv[])
{
char inname[1024];
char outname[1024];

  printf("\nreverb - Copyright 2002 Michael Kohn (mike@naken.cc)\n");

  if (argc<3)
  {
    printf("Usage: reverb <input filename.wav> <output filename.wav> <depth> <decay>\n");
    printf("       depth: optional. defaults to %d\n",DEPTH);
    printf("       decay: optional. defaults to 0.5 (must be 1 or less)\n");
    exit(1);
  }

  depth=DEPTH;
  decay=0.5;

  strcpy(inname,argv[1]);
  strcpy(outname,argv[2]);

  if (argc>3) { depth=atoi(argv[3]); }
  if (argc>4) { decay=atof(argv[4]); }

  reverb(inname,outname);

  return 0;
}



