/* translate various special case sound files to something we can edit 
 * 
 * I'm ignoring propietary or licensed schemes even where the code is publicly available (Rockwell ADPCM, shorten, etc)
 *
 * currently supported:
 *   IEEE text
 *   Mus10 16-bit SAM (mode 4)
 *   IBM CVSD RIFF
 *   HCOM (from Sox)
 *   shortpack NIST
 *   Dvi-Intel ADPCM RIFF (comes in 3 and 4 bit flavors, but just 4-bit here)
 *   MIDI sample dump
 *   Oki (Dialogic) ADPCM (RIFF)
 *   Yamaha TX-16 12-bit(?)
 */

#include "snd.h"

#define TRANS_BUF_SIZE 8192


static int be_snd_checked_write(snd_state *ss, int fd, unsigned char *buf, int bytes)
{
  /* handle little-endian swap if necessary */
#ifdef CLM_LITTLE_ENDIAN
  unsigned char tmp;
  int i;
  for (i=0;i<bytes;i+=2)
    {
      tmp = buf[i];
      buf[i] = buf[i+1];
      buf[i+1] = tmp;
    }
#endif
  return(snd_checked_write(ss,fd,buf,bytes));
}

static int hdr_snd_checked_write(snd_state *ss, int fd, unsigned char *buf, int bytes, int head_bytes)
{
  /* handle little-endian swap if necessary -- assume header portion here up to head_bytes */
#ifdef CLM_LITTLE_ENDIAN
  unsigned char tmp;
  int i;
  for (i=0;i<head_bytes;i+=4)
    {
      tmp = buf[i];
      buf[i] = buf[i+3];
      buf[i+3] = tmp; 
      tmp = buf[i+2];
      buf[i+2] = buf[i+1];
      buf[i+1] = tmp;
   }
#endif
  return(snd_checked_write(ss,fd,buf,bytes));
}


/* -------------------------------- MIDI sample dump -------------------------------- */

static int read_midi_sample_dump(snd_state *ss, char *oldname, char *newname, char *hdr)
{
  int fs,fd,totalin,happy,chans,srate,inp,outp,val = 0,bits,block_count,header_count,state,samples,shift1,shift2,offset;
  short *osp;
  unsigned char *buf;
  chans = 1;
  fd = open(oldname,O_RDONLY,0);
  buf = (unsigned char *)calloc(TRANS_BUF_SIZE,sizeof(char));
  totalin = read(fd,buf,TRANS_BUF_SIZE);
  /* F0 7E <ID> 01 ss ss ee ff ff ff gg gg gg hh hh hh ii ii ii jj f7
   * ss: sample# (LSB MSB), ee: #bits, ff: 1/srate in nsec, gg: samples, hh: loop ii: loop jj: loop 
   * 0000000       f07e 0001 0000 1048 5007 7479 0000 0000
   * 0000020       0000 007f f7f0 7e00 0200 4000 003f 7140
   */
  bits = buf[6];
  srate = (int)(1.0e9 / (float)((buf[7] + (buf[8]<<7) + (buf[9]<<14))));
  samples = (buf[10] + (buf[11]<<7) + (buf[12]<<14));
  fs = creat(newname,0666);
  (*(int *)(hdr+16)) = srate;
  (*(int *)(hdr+20)) = chans;
  if (bits == 16) (*(int *)(hdr+8)) = samples*2; else (*(int *)(hdr+8)) = samples;
  hdr_snd_checked_write(ss,fs,(unsigned char *)hdr,28,28);
  happy = 1;
  inp = 21;
  block_count = 120;
  state = 2;
  header_count = 5;
  outp = 0;
  osp = (short *)hdr;
  /* we could be really wacked out and implement any sample width here */
  if (bits == 16) 
    {
      shift1 = 9; 
      shift2 = 5;
      offset = 32768;
    }
  else 
    {
      shift1 = 1;
      shift2 = 6;
      offset = 128;
    }
  while (happy)
    {
      if (inp >= totalin)
	{
	  if (totalin < TRANS_BUF_SIZE) happy = 0;
	  else {totalin = read(fd,buf,TRANS_BUF_SIZE); inp = 0;}
	}
      if (outp >= TRANS_BUF_SIZE) {be_snd_checked_write(ss,fs,(unsigned char *)hdr,TRANS_BUF_SIZE); osp = (short *)hdr; outp = 0;}
      if (happy)
	{
	  /* MIDI: hardware hackers make a "protocol" ... */
	  if (state != 2) 
	    {
	      block_count--; 
	      if (block_count == 0) 
		{
		  state = 2; 
		  header_count = 7;
		}
	    }
	  switch (state)
	    {
	    case 0: 
	      /* val = buf[inp];  */
	      /* hmmm...  I wonder about this -- the MIDI spec says LSB first,
	       *   but the Goldwave midi sample dump output sends MSB first.
	       * I bet this is a bug 
	       */
	      val = buf[inp] << shift1;
	      state = ((bits == 16) ? 1 : 3); 
	      break;
	    case 1: 
	      /* val |= (buf[inp] << 7);  */
	      val |= (buf[inp] << 2);
	      state = 3; 
	      break;
	    case 2: 
	      header_count--; 
	      if (header_count == 0) 
		{
		  state = 0; 
		  block_count = 121;
		} 
	      break;
	    case 3: 
	      /* val |= (buf[inp] << shift1);  */
	      val |= (buf[inp] >> shift2);
	      state = 0; 
	      (*osp) = (short)(val-offset); 
	      osp++; 
	      outp+=2; 
	      break;
	    }
	  inp++;
	}
    }
  if (outp > 0) be_snd_checked_write(ss,fs,(unsigned char *)hdr,outp);
  close(fs);
  close(fd);
  free(buf);
  return(0);
}



/* -------------------------------- IEEE TEXT -------------------------------- */

static int read_ieee_text(snd_state *ss, char *oldname, char *newname, char *hdr)
{
  /* from untext.c */
  /* look for "%sampling rate: nn.nn KHz\n", also get end of to comment (i.e. data location) */
  char str[32];
  char *buf;
  int fd,fs,totalin;
  int commenting,inp,outp,op,happy,i,j,s0,srate;
  float fsrate;
  short *osp;
  fd = open(oldname,O_RDONLY,0);
  fs = creat(newname,0666);
  buf = (char *)calloc(TRANS_BUF_SIZE,sizeof(char));
  totalin = read(fd,buf,TRANS_BUF_SIZE);      
  commenting = 1;
  inp = 0;
  outp = 24;
  srate = 0;
  op = 0;
  if (buf[0] != '%') {free(buf); close(fd); close(fs); return(-2);} /* not a text file, or messed up in some way */
  while (commenting)
    {
      if (buf[inp] == '%') {op = inp; inp++;}
      else
	{
	  if (buf[inp] == '\n')
	    {
	      if (srate == 0)
		{
		  for (i=op+1,j=0;(i<inp) && (j < 13);i++,j++) str[j] = buf[i];
		  str[13] = '\0';
		  if (strcmp(str,"sampling rate") == 0) 
		    {
		      for (i=op+15,j=0;j<6;i++,j++) str[j] = buf[i];
		      str[6] = '\0';
		      sscanf(str,"%f",&fsrate);
		      srate = fsrate*1000;
		    }
		  else
		    {
		      if (strcmp(str,"Sampling Rate") == 0)
			{
			  for (i=op+15,j=0;j<6;i++,j++) str[j] = buf[i];
			  str[6] = '\0';
			  sscanf(str,"%d",&srate);
			}
		    }
		}
	      inp++;
	      if (buf[inp] != '%') commenting = 0;
	      else
		{
		  hdr[outp] = '\n';
		  outp++;
		}
	    }
	  else
	    {
	      hdr[outp] = buf[inp];
	      outp++;
	      inp++;
	    }
	}
    }
  i=(outp%4);
  outp += i;
  (*(int *)(hdr+4)) = outp;
  if (srate != 0) (*(int *)(hdr+16)) = srate;
  hdr_snd_checked_write(ss,fs,(unsigned char *)hdr,outp,28);
  happy = 1;
  s0 = 0;
  outp = 0;
  osp = (short *)hdr;
  while (happy)
    {
      if (inp >= totalin)
	{
	  if (totalin < TRANS_BUF_SIZE) happy = 0;
	  else {totalin = read(fd,buf,TRANS_BUF_SIZE); inp = 0;}
	}
      if (outp >= TRANS_BUF_SIZE) {be_snd_checked_write(ss,fs,(unsigned char *)hdr,TRANS_BUF_SIZE); osp = (short *)hdr; outp = 0;}
      if (happy)
	{
	  if (buf[inp] == '\n')
	    {
	      str[s0] = '\0';
	      sscanf(str,"%d",&j);
	      (*osp) = (short)j;
	      osp++;
	      outp += 2;
	      inp++;
	      s0 = 0;
	    }
	  else
	    {
	      str[s0] = buf[inp];
	      s0++;
	      inp++;
	    }
	}
    }
  be_snd_checked_write(ss,fs,(unsigned char *)hdr,outp);
  /* update size field? */
  close(fs);
  close(fd);
  free(buf);
  return(0);
}


/* -------------------------------- Mus10 -------------------------------- */

#define PDP_BUF_SIZE (9*1024)

static int read_mus10(snd_state *ss, char *oldname, char *newname, char *hdr)
{
  /* from trans.lisp */
  /* nostalgic code -- 36 bit words, two 16-bit samples, right justified */
  /* or (even more archaeological) 12 bits packed 3 to a 36-bit word */
  unsigned char *buf;
  int fd,fs,totalin,inp,outp,happy,val;
  short *osp;
  float fsrate,fraction;
  int srateH,srateL,sign,exponent,chans,mode;
  fd = open(oldname,O_RDONLY,0);
  fs = creat(newname,0666);
  buf = (unsigned char *)calloc(PDP_BUF_SIZE,sizeof(char));
  totalin = read(fd,buf,PDP_BUF_SIZE);      
  /* read the PDP-10 float srate, nchans, mode, etc */
  /* old header started with 36 bits of 0xaaaaaaaaa */
  srateH = (((buf[4] & 0xF) << 14) | (buf[5]<<6) | (buf[6]>>2));
  srateL = (((buf[6] & 0x3) << 16) | (buf[7]<<8) | (buf[8]));
  /* PDP-10 floating point format was sign in bit 0 , excess 128 exponent in 1-8, fraction in 9-35 */
  if (srateH & 0400000) sign = -1; else sign = 1;
  exponent = ((srateH & 0377000)>>9) - 128;
  fraction = (float)(((srateH & 0777)<<18) | srateL) / pow(2.0,27);
  fsrate = sign * pow(2.0,exponent) * fraction;
  if (fsrate > 6400.0) 
    (*(int *)(hdr+16)) = (int)fsrate;  
  else
    {
      /* perhaps old style header? */
      if (srateH != 0) (*(int *)(hdr+16)) = srateH;
    }
  mode = ((buf[11] & 0x3F)<<12) | (buf[12]<<4) | (buf[13]>>4);
  chans = ((buf[15] & 0x3)<<12) | (buf[16]<<8) | buf[17];
  if (chans == 0) chans = 1;
  (*(int *)(hdr+20)) = chans;
  if ((mode != 4) && (mode != 0)) {free(buf); close(fd); close(fs); fprintf(stderr,"mode: %d", mode); return(-2);} 
  /* 4 = SAM 16-bit packing mode, 0 = 12 bit 3 to a word */
  /* now jump to data start */
  inp = 576;
  hdr_snd_checked_write(ss,fs,(unsigned char *)hdr,28,28);
  happy = 1;
  outp = 0;
  osp = (short *)hdr;
  while (happy)
    {
      if (inp >= totalin)
	{
	  if (totalin < PDP_BUF_SIZE) happy = 0;
	  else {totalin = read(fd,buf,PDP_BUF_SIZE); inp = 0;}
	}
      if (outp >= TRANS_BUF_SIZE) {be_snd_checked_write(ss,fs,(unsigned char *)hdr,TRANS_BUF_SIZE); osp = (short *)hdr; outp = 0;}
      if (happy)
	{
	  if (mode == 4)
	    {
	      /* packed 4 bits junk | 16 bit | 16 bit per each 36 */
	      /* so we grab four at a time here to keep the pointers aligned */
	      /* we've chosen an input buffer size that is a multiple of 9 so that this code need not constantly check bounds */
	      val = ((buf[inp] & 0xF) << 12) | (buf[inp+1] << 4) | (buf[inp+2] >> 4);
	      (*osp) = (short)val; osp++;
	      val = ((buf[inp+2] & 0xF) << 12) | (buf[inp+3] << 4) | (buf[inp+4] >> 4);
	      (*osp) = (short)val; osp++;
	      (*osp) = (short)((buf[inp+5]<<8) | buf[inp+6]); osp++;
	      (*osp) = (short)((buf[inp+7]<<8) | buf[inp+8]); osp++;
	      outp += 8;
	      inp += 9;
	    }
	  else
	    {
	      val = (buf[inp] << 8) | (buf[inp+1] & 0xF0);
	      (*osp) = (short)val; osp++;
	      val = ((buf[inp+1] & 0xF) << 12) | (buf[inp+2] << 4);
	      (*osp) = (short)val; osp++;
	      outp += 4;
	      inp += 3;
	    }
	}
    }
  be_snd_checked_write(ss,fs,(unsigned char *)hdr,outp);
  close(fs);
  close(fd);
  free(buf);
  return(0);
}


/* -------------------------------- IBM CVSD --------------------------------
 *
 * sox11 cvsd.c claims there's a spec for some form of this silliness:
 *      The CVSD format is described in the MIL Std 188 113, which is
 *      available from http://bbs.itsi.disa.mil:5580/T3564
 *
 * it also pushes the bits through a filter, and counts down from bit 7 to 0,
 * but it's definitely different from the CVSD as intended in a wav file.
 */

static int read_ibm_cvsd(snd_state *ss, char *oldname, char *newname, char *hdr)
{
  /* assumed to be in a RIFF file, and that we just read the header via c_read_header */
  /* avg rate gives srate/8 (8 bits per byte) -- can be ignored, can be stereo */
  int fs,fd,loc,totalin,happy,chans,srate,inp,outp,i,chn,byte;
  int *curvals;
  short *osp;
  unsigned char *buf;
  loc = c_snd_header_data_location();
  chans = c_snd_header_chans();
  curvals = (int *)calloc(chans,sizeof(int));
  srate = c_snd_header_srate();
  (*(int *)(hdr+16)) = srate;
  (*(int *)(hdr+20)) = chans;
  fd = open(oldname,O_RDONLY,0);
  fs = creat(newname,0666);
  hdr_snd_checked_write(ss,fs,(unsigned char *)hdr,28,28);
  lseek(fd,loc,0);
  buf = (unsigned char *)calloc(TRANS_BUF_SIZE,sizeof(char));
  totalin = read(fd,buf,TRANS_BUF_SIZE);
  happy = 1;
  inp = 0;
  outp = 0;
  osp = (short *)hdr;
  while (happy)
    {
      if (inp >= totalin)
	{
	  if (totalin < TRANS_BUF_SIZE) happy = 0;
	  else {totalin = read(fd,buf,TRANS_BUF_SIZE); inp = 0;}
	}
      if (outp >= TRANS_BUF_SIZE) {be_snd_checked_write(ss,fs,(unsigned char *)hdr,TRANS_BUF_SIZE); osp = (short *)hdr; outp = 0;}
      if (happy)
	{
	  /* each byte becomes 8 samples */
	  chn = 0;
	  byte = buf[inp]; inp++;
	  for (i=0;i<8;i++)
	    {
	      /* are the bits consumed low to high or high to low? assume low to high for now (count i down from 7 to 0 if high to low) */
	      if (byte & (1<<i)) curvals[chn]++; else curvals[chn]--;
	      (*osp) = (short)curvals[chn]; osp++; chn++;
	      if (chn == chans) chn = 0;
	    }
	  outp+=16;
	}
    }
  be_snd_checked_write(ss,fs,(unsigned char *)hdr,outp);
  close(fs);
  close(fd);
  free(curvals);
  free(buf);
  return(0);
}


/* -------------------------------- HCOM (from Sox) -------------------------------- */

static int read_hcom(snd_state *ss, char *oldname, char *newname, char *hdr)
{
  short **d;
  short *osp;
  unsigned int *isp;
  int dc,di,bits,inp,outp,happy,totalin,tmp;
  unsigned int curval = 0;
  int i,sample,size,datum,count;
  unsigned char *buf;
  int fd,fs;
  fd = open(oldname,O_RDONLY,0);
  fs = creat(newname,0666);
  lseek(fd,132,0);
  buf = (unsigned char *)calloc(TRANS_BUF_SIZE,sizeof(char));
  read(fd,buf,18);  /* count sum type div size */
#ifdef CLM_LITTLE_ENDIAN
  /* file is assumed to be big-endian since written on a Mac presumably */
  tmp=buf[0]; buf[0]=buf[3]; buf[3]=tmp; tmp=buf[1]; buf[1]=buf[2]; buf[2]=tmp;
  tmp=buf[8]; buf[8]=buf[11]; buf[11]=tmp; tmp=buf[9]; buf[9]=buf[10]; buf[10]=tmp;
  tmp=buf[16]; buf[16]=buf[17]; buf[17]=tmp;
#endif
  count = (*(int *)buf) - 1;
  dc = (*(int *)(buf+8));
  size = (*(short *)(buf+16));
  d = (short **)calloc(size,sizeof(short *));
  read(fd,buf,size*4+2); /* 2 for pad byte + first sample */
#ifdef CLM_LITTLE_ENDIAN
  for (i=0;i<size*4+2;i+=2) {tmp=buf[i]; buf[i]=buf[i+1]; buf[i+1]=tmp;}
#endif
  osp = (short *)buf;
  for (i=0;i<size;i++) 
    {
      d[i] = (short *)calloc(2,sizeof(short));
      d[i][0] = (*osp); osp++;
      d[i][1] = (*osp); osp++;
    }
  sample = (*osp) & 0xff;
  di = 0;
  totalin=read(fd,buf,TRANS_BUF_SIZE);
#ifdef CLM_LITTLE_ENDIAN
  for (i=0;i<totalin;i+=4) {tmp=buf[i]; buf[i]=buf[i+3]; buf[i+3]=tmp; tmp=buf[i+1]; buf[i+1]=buf[i+2]; buf[i+2]=tmp;}
#endif
  hdr_snd_checked_write(ss,fs,(unsigned char *)hdr,28,28);
  osp = (short *)hdr;
  isp = (unsigned int *)buf;
  happy = 1;
  inp = 0;
  outp = 2;
  (*osp) = (sample - 128) * 0x100; osp++;
  bits = 0;
  while ((happy) && (count>0))
    {
      if (inp >= totalin)
	{
	  if (totalin < TRANS_BUF_SIZE) 
	    happy = 0;
	  else 
	    {
	      totalin = read(fd,buf,TRANS_BUF_SIZE); 
#ifdef CLM_LITTLE_ENDIAN
	      for (i=0;i<totalin;i+=4) {tmp=buf[i]; buf[i]=buf[i+3]; buf[i+3]=tmp; tmp=buf[i+1]; buf[i+1]=buf[i+2]; buf[i+2]=tmp;}
#endif
	      inp = 0; 
	      isp = (unsigned int *)buf;
	    }
	}
      if (outp >= TRANS_BUF_SIZE) {be_snd_checked_write(ss,fs,(unsigned char *)hdr,TRANS_BUF_SIZE); osp = (short *)hdr; outp = 0;}
      if (happy)
	{
	  if (bits == 0) {curval = (*isp); isp++; inp+=4; bits = 32;}
	  if (curval & 0x80000000) di = d[di][1]; else di = d[di][0];
	  curval = curval << 1;
	  bits--;
	  if(d[di][0] < 0) 
	    {
	      datum = d[di][1];
	      if (!dc) sample = 0;
	      sample = (sample + datum) & 0xff;
	      count--;
	      if (sample == 0) (*osp) = (short)(-127 * 0x100);
	      else (*osp) = (short)((sample - 128) * 0x100);
	      osp++;
	      outp+=2;
	      di = 0;
	    }
	}
    }
  be_snd_checked_write(ss,fs,(unsigned char *)hdr,outp);
  close(fs);
  close(fd);
  for (i=0;i<size;i++) free(d[i]);
  free(d);
  free(buf);
  return(0);
}


/* -------------------------------- NIST shortpack -------------------------------- */

static unsigned short log2s[] = {1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};

static int read_nist_shortpack(snd_state *ss, char *oldname, char *newname, char *hdr)
{
  /* assume all relevant header stuff is ready via c_read_header */
  int fs,fd,totalin,happy,chans,srate,inp,outp,i = 0,k,num,bits = 0,out,els = 0,tmp;
  short *osp,*isp;
  unsigned short *ptr = NULL,*stop,*start,*kptr;
  short temp = 0;
  unsigned char negative;
  unsigned char *buf;
  chans = c_snd_header_chans();
  srate = c_snd_header_srate();
  (*(int *)(hdr+16)) = srate;
  (*(int *)(hdr+20)) = chans;
  fd = open(oldname,O_RDONLY,0);
  fs = creat(newname,0666);
  hdr_snd_checked_write(ss,fs,(unsigned char *)hdr,28,28);
  lseek(fd,1024,0);                       /* NIST header always 1024 bytes */
  buf = (unsigned char *)calloc(TRANS_BUF_SIZE,sizeof(char));
  totalin = read(fd,buf,TRANS_BUF_SIZE);
  happy = 1;
  inp = 0;
  outp = 0;
  num = 0;
  osp = (short *)hdr;
  isp = (short *)buf;
  start = &(log2s[15]);
  stop = log2s;
  while (happy)
    {
      /* now the shortpack algorithm, taken from wavio's shortpack_io.c */
      if (num == 0)
	{
	  num = (int)buf[inp]; inp++;
	  bits = (int)buf[inp]; inp++;
	  isp++; 
	  if (inp>=totalin) {totalin = read(fd,buf,TRANS_BUF_SIZE); inp = 0; isp = (short *)buf;}
#ifdef CLM_LITTLE_ENDIAN 
	  tmp=buf[inp]; buf[inp]=buf[inp+1]; buf[inp+1]=tmp;
#endif
	  temp = (*isp); isp++; inp+=2;
	  if (inp>=totalin) {totalin = read(fd,buf,TRANS_BUF_SIZE); inp = 0; isp = (short *)buf;}
	  ptr = start;
	  i = 0;
	  els = (num * (bits + 1)) / 16.0;
	  if ((num * (bits + 1)) % 16 != 0) els++;
	  els--;
	}
      else
	{
	  /* get next sample */
	  out = 0;
	  negative = ((temp & *(ptr--)) != 0);
	  if (ptr < stop)
	    {
	      ptr = start;
	      if (els > 0)
		{
#ifdef CLM_LITTLE_ENDIAN 
		  tmp=buf[inp]; buf[inp]=buf[inp+1]; buf[inp+1]=tmp;
#endif
		  temp = (*isp); isp++; inp+=2;
		  if (inp>=totalin) {totalin = read(fd,buf,TRANS_BUF_SIZE); inp = 0; isp = (short *)buf;}
		  els--;
		}
	    }
	  kptr = &(log2s[bits - 1]);
	  for (k = bits + 1; (--k) > 0;)
	    {
	      if ((temp & *(ptr--)) != 0) out |= *kptr;
	      kptr--;
	      if (ptr < stop)
		{
		  ptr = start;
		  if (els > 0)
		    {
#ifdef CLM_LITTLE_ENDIAN 
		      tmp=buf[inp]; buf[inp]=buf[inp+1]; buf[inp+1]=tmp;
#endif
		      temp = (*isp); isp++; inp+=2;
		      if (inp>=totalin) {totalin = read(fd,buf,TRANS_BUF_SIZE); inp = 0; isp = (short *)buf;}
		      els--;
		    }
		}
	    }
	  if (negative)
	    if (out != 0) (*osp) = -out;
	    else (*osp) = 32767;
	  else (*osp) = out;
	  osp++; outp+=2;
	  i++;
	  if (i == num) num=0;
	}
      if (inp >= totalin)
	{
	  if (totalin < TRANS_BUF_SIZE) happy = 0;
	  else {totalin = read(fd,buf,TRANS_BUF_SIZE); inp = 0; isp = (short *)buf;}
	}
      if (outp >= TRANS_BUF_SIZE) {be_snd_checked_write(ss,fs,(unsigned char *)hdr,TRANS_BUF_SIZE); osp = (short *)hdr; outp = 0;}
    }
  be_snd_checked_write(ss,fs,(unsigned char *)hdr,outp);
  close(fs);
  close(fd);
  free(buf);
  return(0);
}


/* -------------------------------- Intel ADPCM --------------------------------
 *
 * described in detail Microsoft RIFF docs.  This code assumes bits=4 (it can also be 3).
 *
 * in an RIFF-wave file, these are stored as block_align sized blocks, each with a
 * header storing the current state.  These can be multi-channel, but we're handling
 * only mono until someone complains.
 */

static int indexTable[16] = {-1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8};
static int stepsizeTable[89] = {7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
				50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
				337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
				2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
				5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
				15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767};

static int adpcm_decoder(unsigned char *indata, short *outdata, int totalbytes)
{ /* this follows the Microsoft documentation to the letter, but does not seem to work with the SGI dmconvert output? */
  /* there appears to be way too many sign bits on */
  unsigned int delta,inputbuffer = 0;
  int step,valpred,vpdiff,index,bufferstep,i,j,happy;
  valpred = little_endian_short(*((short *)indata));
  index = indata[2];
  bufferstep = 0;
  happy = 1;
  j=4;
  i=1;
  outdata[0] = valpred;
  while (happy)
    {
      if (bufferstep) 
	{
	  delta = inputbuffer & 0xf;
	  if (j == totalbytes) happy=0;
	} 
      else 
	{
	  inputbuffer = indata[j]; j++; 
	  delta = (inputbuffer>>4) & 0xf;
	}
      bufferstep = !bufferstep;
      step = stepsizeTable[index];
      vpdiff = (step>>3);
      if (delta & 1) vpdiff += (step>>2);
      if (delta & 2) vpdiff += (step>>1);
      if (delta & 4) vpdiff += step;
      if (delta & 8) valpred -= vpdiff; else valpred += vpdiff;
      if (valpred > 32767)  valpred = 32767; else if (valpred < -32768)  valpred = -32768;
      outdata[i] = valpred; i++;
      index += indexTable[delta];
      if (index < 0) index = 0; else if (index > 88) index = 88;
    }
  return(i);
}


static int read_dvi_adpcm(snd_state *ss, char *oldname, char *newname, char *hdr)
{
  int fs,fd,loc,totalin,chans,srate,blksiz,samps,samps_read;
  unsigned char *buf;
  loc = c_snd_header_data_location();
  chans = c_snd_header_chans();
  blksiz = c_snd_header_block_align();
  samps = c_snd_header_fact_samples();
  if ((chans != 1) || (c_snd_header_bits_per_sample() != 4)) return(-1);
  srate = c_snd_header_srate();
  (*(int *)(hdr+16)) = srate;
  (*(int *)(hdr+20)) = chans;
  fd = open(oldname,O_RDONLY,0);
  fs = creat(newname,0666);
  hdr_snd_checked_write(ss,fs,(unsigned char *)hdr,28,28);
  lseek(fd,loc,0);
  buf = (unsigned char *)calloc(blksiz,sizeof(unsigned char));
  samps_read = 0;
  while (samps > 0)
    {
      totalin = read(fd,buf,blksiz);
      if (totalin < blksiz) break;
      samps_read = adpcm_decoder(buf,(short *)hdr,totalin);
      be_snd_checked_write(ss,fs,(unsigned char *)hdr,samps_read*2);
      samps -= samps_read;
    }
  close(fs);
  close(fd);
  free(buf);
  return(0);
}

/* --------------------------------Oki (Dialogic) ADPCM --------------------------------
 *
 * from vox.tar.gz:
 *   "PC Telephony - The complete guide to designing, building and programming systems
 *    using Dialogic and Related Hardware" by Bob Edgar. pg 272-276.
 */

struct oki_adpcm_status {short last; short step_index;};

static short oki_step_size[49] = { 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41,
     45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173,
     190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658,
     724, 796, 876, 963, 1060, 1166, 1282, 1408, 1552 };

static short oki_adjust[8]={-1,-1,-1,-1,2,4,6,8};

static short oki_adpcm_decode(char code, struct oki_adpcm_status *stat) 
{
  short diff,E,SS,samp;
  SS = oki_step_size[stat->step_index];
  E = SS/8;
  if (code & 0x01) E += SS/4;
  if (code & 0x02) E += SS/2;
  if (code & 0x04) E += SS;
  diff = (code & 0x08) ? -E : E;
  samp = stat->last + diff;
  if (samp > 2048) samp = 2048;
  if (samp < -2048) samp = -2048;
  stat->last = samp;
  stat->step_index += oki_adjust[code & 0x07];
  if (stat->step_index < 0) stat->step_index = 0;
  if (stat->step_index > 48) stat->step_index = 48;
  return(samp<<4);
}

static int read_oki_adpcm(snd_state *ss, char *oldname, char *newname, char *hdr)
{
  int fs,fd,loc,i,j,totalin,chans,srate,blksiz,samps,samps_read;
  unsigned char *buf;
  short *buf1;
  struct oki_adpcm_status stat;
  loc = c_snd_header_data_location();
  chans = c_snd_header_chans();
  blksiz = c_snd_header_block_align();
  if (blksiz == 0) blksiz=256;
  buf = (unsigned char *)calloc(blksiz,sizeof(unsigned char));
  buf1 = (short *)calloc(blksiz*2,sizeof(short));
  samps = c_snd_header_fact_samples();
  if (chans != 1) return(-1);
  srate = c_snd_header_srate();
  (*(int *)(hdr+16)) = srate;
  (*(int *)(hdr+20)) = chans;
  fd = open(oldname,O_RDONLY,0);
  fs = creat(newname,0666);
  hdr_snd_checked_write(ss,fs,(unsigned char *)hdr,28,28);
  lseek(fd,loc,0);
  samps_read = 0;
  stat.last = 0;
  stat.step_index = 0;
  while (samps > 0)
    {
      totalin = read(fd,buf,blksiz);
      if (totalin <= 0) break;
      for (i=0,j=0;i<totalin;i++)
	{
	  /* samps_read will be twice totalin because these are 4-bit quantities */
	  buf1[j++] = oki_adpcm_decode((buf[i]>>4) & 0x0f,&stat);
	  buf1[j++] = oki_adpcm_decode(buf[i]&0x0f,&stat);
	}
      samps_read = totalin*2;
      be_snd_checked_write(ss,fs,(unsigned char *)buf1,samps_read*2);
      samps -= samps_read;
    }
  close(fs);
  close(fd);
  free(buf);
  free(buf1);
  return(0);
}

/* -------------------------------- 12 bit cases --------------------------------
 */

static int read_12bit(snd_state *ss, char *oldname, char *newname, char *hdr)
{
  int loc,chans,samps,totalin,i,j,fs,fd;
  unsigned char *buf;
  short *buf1;

  loc = c_snd_header_data_location();
  chans = c_snd_header_chans();
  samps = c_snd_header_data_size();
  (*(int *)(hdr+16)) = c_snd_header_srate();
  (*(int *)(hdr+20)) = chans;

  fd = open(oldname,O_RDONLY,0);
  fs = creat(newname,0666);
  hdr_snd_checked_write(ss,fs,(unsigned char *)hdr,28,28);
  lseek(fd,loc,0);
  buf = (unsigned char *)calloc((int)(TRANS_BUF_SIZE*1.5),sizeof(unsigned char));
  buf1 = (short *)calloc(TRANS_BUF_SIZE,sizeof(short));

  while (samps > 0)
    {
      totalin = read(fd,buf,(int)(TRANS_BUF_SIZE*1.5));
      if (totalin <= 0) break;
      for (i=0,j=0;i<totalin;i+=3,j+=2)
	{
	  buf1[j] = (signed short)((buf[i]<<8) + (buf[i+1]&0xf0));
	  buf1[j+1] = (signed short)((buf[i+2]<<8) + ((buf[i+1]&0xf)<<4));
	  /* bizarre!  -- this is the packing that Yamaha is using?!? */
	  /* or is Convert 1.4 screwed up? */
	}
      be_snd_checked_write(ss,fs,(unsigned char *)buf1,j*2);
      samps -= j;
    }
  close(fs);
  close(fd);
  free(buf);
  free(buf1);
  return(0);
}


/*  -------------------------------- AVI --------------------------------
 *
 * data is squirreled away in ##wb data blocks somewhere within a LIST chunk
 * we have to mimic the header reader to find these guys one by one.
 * we assume we've got 16-bit linear little endian data here all in a single sequence of 'wb' blocks.
 * (in the 'rec' case, audio and video data can be interleaved, but that's too bad)
 */

static int read_avi(snd_state *ss, char *oldname, char *newname, char *hdr)
{
  int totalin,fs,fd,cksize,num,happy;
#ifndef CLM_LITTLE_ENDIAN
  int i;
#endif
  short *buf;
  unsigned char *hdrbuf;
  (*(int *)(hdr+16)) = c_snd_header_srate();
  (*(int *)(hdr+20)) = c_snd_header_chans();
  fd = open(oldname,O_RDONLY,0);
  fs = creat(newname,0666);
  hdr_snd_checked_write(ss,fs,(unsigned char *)hdr,28,28);
  hdrbuf = (unsigned char *)calloc(8,sizeof(unsigned char));
  buf = (short *)calloc(TRANS_BUF_SIZE,sizeof(short));
  lseek(fd,c_snd_header_data_location(),0L);
  happy = 1;
  while (happy)
    {
      totalin = read(fd,hdrbuf,8);
      if (totalin < 0) break;
      cksize = little_endian_int((*(int *)(hdrbuf+4)));
      if ((hdrbuf[2] == 'w') && (hdrbuf[3] == 'b'))
	{
	  while (cksize > 0)
	    {
	      if (TRANS_BUF_SIZE*2 > cksize)
		num = cksize;
	      else num = TRANS_BUF_SIZE*2;
	      totalin = read(fd,(unsigned char *)buf,num);
	      if (totalin < 0) 
		{
		  happy = 0; 
		  break;
		}
	      else 
		{
#ifndef CLM_LITTLE_ENDIAN
		  for (i=0;i<totalin/2;i++) buf[i] = little_endian_short(buf[i]);
#endif		  
		  be_snd_checked_write(ss,fs,(unsigned char *)buf,totalin);
		}
	      cksize -= num;
	    }
	}
      else break;
    }
  close(fs);
  close(fd);
  free(buf);
  free(hdrbuf);
  return(0);
}



/* -------------------------------- TRANSLATE -------------------------------- */

int snd_translate(snd_state *ss, char *oldname, char *newname)
{
  /* read oldname, translate to newname as 16-bit linear NeXT file */
  /* called from snd-file.c */
  int type,orig,err;
  char *hdr = NULL;
  hdr = (char *)calloc(TRANS_BUF_SIZE,sizeof(char));
  /* set up default output header */
  (*(int *)hdr) = 0x2e736e64; /* .snd */
  (*(int *)(hdr+4)) = 28;     /* data location */
  (*(int *)(hdr+8)) = 0;      /* bytes in data portion */
  (*(int *)(hdr+12)) = 3;     /* 16-bit linear */
  (*(int *)(hdr+16)) = 22050;
  (*(int *)(hdr+20)) = 1;     /* chans */

  err = c_read_header(oldname); /* returns -1 if failure to open (i.e. file does not exist or something) */
  if (err == 0)
    {
      type = c_snd_header_type();
      orig = c_snd_header_original_format();
      /* err returns -2 if translation is not yet possible */
      switch (type)
	{
	case MIDI_sample_dump: err = read_midi_sample_dump(ss,oldname,newname,hdr); break;
	case IEEE_sound_file: err = read_ieee_text(ss,oldname,newname,hdr); break;
	case MUS10_sound_file: err = read_mus10(ss,oldname,newname,hdr); break;
	case HCOM_sound_file: err = read_hcom(ss,oldname,newname,hdr); break;
	case Yamaha_TX16_sound_file: err = read_12bit(ss,oldname,newname,hdr); break;
	case AVI_sound_file: err = read_avi(ss,oldname,newname,hdr); break;
	case RIFF_sound_file:
	  switch (orig)
	    {
	    case RIFF_IBM_CVSD: err = read_ibm_cvsd(ss,oldname,newname,hdr); break;
	    case RIFF_Intel_ADPCM: err = read_dvi_adpcm(ss,oldname,newname,hdr); break;
	    case RIFF_Oki_ADPCM: err = read_oki_adpcm(ss,oldname,newname,hdr); break;
	    default: err = -2; break;
	    }
	  break;
	case NIST_sound_file:
	  if (orig == NIST_shortpack) read_nist_shortpack(ss,oldname,newname,hdr); 
	  break;
	case AIFF_sound_file:
	case NeXT_sound_file:
	case Matlab_sound_file:
	  /* assume all vectors are channels */
	default: err = -2; break;
	}
    }
  free(hdr);
  if (err != 0) fprintf(stderr,"\nerr: %d",err);
  return(err);
}



