/*  
 *  This file is part of abc2ps, 
 *  Copyright (C) 1996-2007  Michael Methfessel
 *  See file abc2ps.c for details.
 */

/* derived from sox to write .wav file in PCM format */

#include <string.h>		/* Included for strncmp */
#include <stdlib.h>		/* Included for malloc and free */
#include <stdio.h>

#include "abc2ps.h"

#define	LONG	long
#define ULONG	unsigned long
#define	WAVE_FORMAT_PCM         0x0001 

LONG    numSamples;
LONG    rate;            /* sample rate */
int     channels;        /* number of channels */
int     bps;             /* bits per sample */
FILE   *fp;

/* little-endian output routines */
void wlshort(FILE *fp, unsigned short us)
{
  putc (us, fp);
  putc (us >> 8, fp);
}

void wllong (FILE *fp, ULONG ul)
{
  char datum;
  datum = (char) (ul) & 0xff;
  putc(datum, fp);
  datum = (char) (ul >> 8) & 0xff;
  putc(datum, fp);
  datum = (char) (ul >> 16) & 0xff;
  putc(datum, fp);
  datum = (char) (ul >> 24) & 0xff;
  putc(datum, fp);
}

void wavwritehdr (int sh);

/* --- wavstartwrite --- */
void wavstartwrite (FILE *fpin, int rate_in, int bps_in, int chan_in) 
{
  fp = fpin;
  rate = rate_in;
  channels = chan_in;
  bps = bps_in;
  numSamples = 0;
  wavwritehdr (0);
}

/* --- wavwritehdr --- */
void wavwritehdr (int second_header) 
{

  /* wave file characteristics */
  unsigned short wFormatTag = 0;   /* data format */
  unsigned short wChannels;        /* number of channels */
  ULONG  wSamplesPerSecond;        /* samples per second per channel */
  ULONG  wAvgBytesPerSec;          /* estimate of bytes per second needed */
  unsigned short wBlockAlign;      /* byte alignment of a basic sample block */
  unsigned short wBitsPerSample;   /* bits per sample */
  ULONG  data_length;              /* length of sound data in bytes */
  ULONG  bytespersample; 	   /* bytes per sample (per channel) */

  wBitsPerSample = bps;
  wSamplesPerSecond = rate;
  bytespersample = (wBitsPerSample + 7)/8;
  wAvgBytesPerSec = rate * channels * bytespersample;
  wChannels = channels;
  wBlockAlign = channels * bytespersample;
  wFormatTag = WAVE_FORMAT_PCM;

  /* use max length value first time */
  if (! second_header)	 
    data_length = 0x7fffffffL - (8+16+12);
  /* fixup with real length */
  else 
    data_length = bytespersample * numSamples * channels;
  
  /* write out header info */
  fputs   ("RIFF", fp);
  wllong  (fp, data_length + 8+16+12);	/* Waveform chunk size: FIXUP(4) */
  fputs   ("WAVE", fp);
  fputs   ("fmt ", fp);
  wllong  (fp, (LONG)16);		/* fmt chunk size */
  wlshort (fp, wFormatTag);
  wlshort (fp, wChannels);
  wllong  (fp, wSamplesPerSecond);
  wllong  (fp, wAvgBytesPerSec);
  wlshort (fp, wBlockAlign);
  wlshort (fp, wBitsPerSample);
  fputs ("data", fp);
  wllong (fp, data_length);		/* data chunk size: FIXUP(40) */
  
  if (vb > 3) {
    if (! second_header) {
      printf ("Writing Wave file: PCM format, %d channel%s, %ld samples/sec\n",
              wChannels, wChannels == 1 ? "" : "s", wSamplesPerSecond);
      printf("  %ld byte/sec, %d block align, %d bits/sample\n",
             wAvgBytesPerSec, wBlockAlign, wBitsPerSample);
    } 
    else
      printf("Finished writing Wave file, %lu data bytes\n", data_length);
  }
}


/* --- wavdump: dump for plotting --- */
void wavdump (FILE *fpnt, float *buf, int len) 
{
  FILE *dump;
  int i, step, top, num, i0, i1, i2;
  float tm, tau, fac, tmin, tmax;
  static int done=0, ncall=0;
  
  if (done) return;
  ncall ++;

  fac = 1;
  if (bps == 16) fac = 44.1/8.0;

  i0 = -1;
  for (i=0; i<len; i++) {
    if (buf[i] > 0.001) { i0 = i; break; }
  }

  if (i0 < 0) return;
  i0 = i0 - fac * 10;
  if (i0 < 0) i0 = 0;

  tau = 1.0 / rate;
  tmin = i0 * tau;
  tmax = tmin + .02; 
  step = fac; 

  step = 3*fac;
  tmax = tmin + 1;

/*|   tmax = tmin + 1.00; |*/
/*|   step = 3 * fac; |*/

  top  = 3000;

  i1 = tmin / tau;
  i2 = tmax / tau;
  if (i2 > len) i2 = len;

/*|   printf ("i0=%d t: %.4f to %.4f  i: %d to %d\n", i0, tmin, tmax, i1, i2); |*/

/*|   printf ("Dump rate=%d tau=%.6f i0=%d top=%d step=%d\n", |*/
/*|           rate, tau, i0, top, step); |*/

    
  if ((dump = fopen ("DUMP","w")) == NULL) {      
    printf ("+++ Cannot open dump file\n");
    return;
  }

  for (i=i1,num=0; (num<top) && (i<i2); i+=step) {
    tm = i * tau;
    fprintf (dump, "%f %f\n", tm, buf[i]);
    num ++;
  }


  done = 1;
  printf ("Dumped %d samples (call %d)\n"
          "%d to %d, step=%d, time %.3f to %.3f\n", 
          num, ncall, i0, i, step, i0*tau, tm);
  fclose (dump);
  
}


/* --- wavwrite --- */
void wavwrite (FILE *fpnt, float *buf, int len) 
{
  int i, c;
  short w;
  char *b;

/*| printf ("  wavwrite len=%d samps=%d bps=%d\n", len, numSamples, bps);|*/
  numSamples += len; 

  if (bps == 8) {
    for (i=0; i<len; i++) {
      c = 256 * 0.5 * (buf[i] + 1.0); 
      fputc (c, fpnt);
      if (channels == 2) fputc (c, fpnt);
    }
    return;
  }

  /* patch: reduce volume instead of using AGC */

  if ((bps == 16) && (channels == 1)) {
    b = (char *)&w;
    for (i=0; i<len; i++) {
      w = 32700 * buf[i];
#if LIL_ENDIAN==0
      fputc (*(b+1), fpnt);
      fputc (*b,     fpnt);
#else
      fputc (*b,     fpnt);
      fputc (*(b+1), fpnt);
#endif
    }
    return;
  }

  if ((bps == 16) && (channels == 2)) {
    b = (char *)&w;
    for (i=0; i<len; i++) {
      w = 32700 * buf[i];
#if LIL_ENDIAN==0
      fputc (*(b+1), fpnt);
      fputc (*b,     fpnt);
      fputc (*(b+1), fpnt);
      fputc (*b,     fpnt);
#else
      fputc (*b,     fpnt);
      fputc (*(b+1), fpnt);
      fputc (*b,     fpnt);
      fputc (*(b+1), fpnt);
#endif
    }
    return;
  }

  printf ("+++ wavwrite: cannot handle bps=%d channels=%d\n", bps, channels);
}


/* --- wavstopwrite --- */
void wavstopwrite () 
{
  /* All samples are already written out. */
  /* If file header needs fixing up, for example it needs the */
  /* the number of samples in a field, seek back and write them here. */

  if (fseek(fp, 0L, 0) != 0)
    rx("wavstopwrite: Cannot rewind file to rewrite .wav header");
  wavwritehdr (1);
}




