#include "hacks.h"
#define min(x,y)    (x > y) ? y : x


/* will need to link in floating point libraries */
/* runs on motorola: expects CDDA files to be motorola as well. */


/* global vars */
static char amiga_version[] = "$VER: Version 1.0\n";
WORD *buffer;       /* file i/o buffer */
WORD *fade_buffer;  /* buffer for crossfade */
long fade_time;
long file_length;


/* forward references */
void usage();
void usage_exit();
void crossfade(WORD *, WORD *, long);
int copy_data(FILE *, FILE *, int);


/* start of actual program */

void usage()
{
  fprintf(stderr, "usage: fade_cdda <fadetime> <file1> <file2> [<fileN>]\
 <destfile>\nFade time is in samples (44100 samples for 1 second fade).  ");
  fprintf(stderr, "all files must be 16-bit motorola byte-order stereo\
 CDDA!\n");
}


void usage_exit()
{
  usage();
  exit(DOOPS);
}


/* Crossfade STEREO sample data */
void crossfade(fadefrom, fadeto, len)
WORD *fadefrom;
WORD *fadeto;
long len;  /* length in stereo samples (4x bytes) */
{
  float temp, temp2;
  unsigned long x, y;

  /* fade from fadefrom to fadeto, putting result in fadefrom */
  for (x = 0, y = 0; x neq len; x++) {
    temp2 = (float) x / (float) len;
    temp = (temp2 * fadeto[y]) + ((1.0 - temp2) * fadefrom[y]);
    fadefrom[y] = temp;
    y++;
    temp = (temp2 * fadeto[y]) + ((1.0 - temp2) * fadefrom[y]);
    fadefrom[y] = temp;
    y++;
  }
}


int copy_data(f1, f2, pastfirst)  /* files already opened */
FILE *f1;
FILE *f2;
int pastfirst;
/* pastfirst is zero when starting on first file.  after that it is 1. */
{
  long copylength, file_size, readthis, x;
  int status;

  if ((status = fseek(f1, 0L, SEEK_END)) eq -1) {  /* go to end of file */
    fprintf(stderr, "fseek error 1\n");
    return(DOOPS);
  }
  if ((file_size = ftell(f1)) eq -1L) {            /* get size */
    fprintf(stderr, "ftell error\n");
    return(DOOPS);
  }
  if ((status = fseek(f1, 0L, SEEK_SET)) eq -1) {  /* rewind */
    fprintf(stderr, "fseek error 2\n");
    return(DOOPS);
  }
  file_size /= 4;  /* convert byte size to stereo word size */
  if ((fade_time * 2) > file_size) {  /* time for fade out plus fade_in */
    fprintf(stderr, "error: file is too small for this fade time\n");
    return(DOOPS);
  }
  /* read first block ... */
  if ((status = fread(buffer, 4, (size_t) fade_time, f1)) neq fade_time) {  
    fprintf(stderr, "file read error 1\n");
    return(DOOPS);
  }
  if (pastfirst eq 0) {  /* if first file, don't fade */
    fprintf(stderr, "skipping fade on first file only\n");
    if((status = fwrite(buffer, 4, (size_t) fade_time, f2)) neq fade_time) {
      fprintf(stderr, "file write error 1\n");
      return(DOOPS);
    }
  } else {          /* if not first file, must fade */
    crossfade(fade_buffer, buffer, fade_time);
    fwrite(fade_buffer, 4, (size_t) fade_time, f2);
  }

  /* copy rest of file, up until last bit. */
  copylength = file_size - (2 * fade_time);
  for (x = copylength; x neq 0; ) {
    readthis = min(x, fade_time);     /* test the min macro */
    if ((status = fread(buffer, 4, (size_t) readthis, f1)) neq readthis) {
      fprintf(stderr, "read error 2\n");
      return(DOOPS);
    }
    if ((status = fwrite(buffer, 4, (size_t) readthis, f2)) neq readthis) {
      fprintf(stderr, "file write error 2\n");
      return(DOOPS);
    }
    x -= readthis;
  }
  /* read last block of file into fade_buffer, instead of buffer */
  if ((status = fread(fade_buffer, 4, (size_t) fade_time, f1)) neq
         fade_time) {
    fprintf(stderr, "file write error 3\n");
    return(DOOPS);
  }
  /* do nothing to the fade buffer.  Leave it for the next call */
  return(OKDOKEY);
}


main(argc, argv)
int argc;
char *argv[];
{
  FILE *f1, *f2;
  long x;
  int status;

  if (argc eq 0)
    exit(DOOPS);
  if (argc < 5)
    usage_exit();
  /* could be any number of files, doh! */

  /* first: parse the fade time */
  fade_time = atol(argv[1]);
  if ((fade_time < 1) || (fade_time > 1764000)) {  /* 40 secs is max fade */
    fprintf(stderr, "%d is a bad fade time must be (1 - 1764000)\n",
    fade_time);
  }
  /* a zero fade time would be very bad: use join instead */
  fade_buffer = calloc( (size_t) fade_time, 4);   /* fade buffer */
  if (fade_buffer eq NULL) {
    fprintf(stderr, "memory failure 1\n");
    exit(DOOPS);
  }
  buffer = calloc( (size_t) fade_time, 4);        /* file i/o buffer */
  if (buffer eq NULL) {
    free(fade_buffer);
    fprintf(stderr, "memory failure 2\n");
    exit(DOOPS);
  }

  /* open output file FIRST */
  if ((f2 = fopen(argv[argc - 1], "wb")) eq NULL) {
    fprintf(stderr, "error creating output file \"%s\"\n", argv[argc - 1]);
    exit(DOOPS);
  }
  if (fade_time < 8192) {
    if ((status = setvbuf(f2, NULL, _IOFBF, 32768)) neq 0) {
      free(buffer);
      free(fade_buffer);
      fprintf(stderr, "setvbuf error on output file\n");
      exit(DOOPS);
    }
  }
  for (x = 2; x neq (argc - 1); x++) {  /* loop through all input files */
    if ((f1 = fopen(argv[x], "rb")) eq NULL) {
      fprintf(stderr, "error opening input file %d: \"%s\"\n", x, argv[x]);
      exit(DOOPS);
    }
    fprintf(stderr, "processing \"%s\"\n", argv[x]);
    if (x eq 2) {  /* skip fade for start of first file */
      if ((status = copy_data(f1, f2, 0)) eq DOOPS)
        exit(status);
    } else {  /* fade for all files but the first */
      if ((status = copy_data(f1, f2, 1)) eq DOOPS)
        exit(status);
    }
    fclose(f1);
  }
  /* Done with loop, but the fade buffer contains the tail end of the last
     input file.  We are not going to fade it, so we should just write it */
  if ((status = fwrite(fade_buffer, 4, (size_t) fade_time, f2)) neq
          fade_time) {
    fprintf(stderr, "error writing last bit to file\n");
    exit(DOOPS);
  }
  fclose(f2);
  free(fade_buffer);
  free(buffer);
  fprintf(stderr, "\033[7m Done \033[m\n");
  exit(OKDOKEY);
}


/* actual end of this file */
