/*		 ugen developed by matt ingalls, ... 		*/
/*							 	*/
/*	diskin	-	a new soundin that shifts pitch		*/
/*		based on the old soundin code			*/

/* Modified 1998 June 5 by Damien Miller & Robin Whittle        */
/* to compile under glibc, rather than the earlier C library.   */
/* One change only - search for "glibc"                         */
/* diskin.c and wave.c both used tell()                         */

#include "cs.h"
#include "soundio.h"
#include "diskin.h"
extern  char    *retfilnam;
extern  HEADATA *readheader(int, char *, SOUNDIN*);
extern  void bytrev2(char*, int);
extern  void bytrev4(char*, int);
extern  char *getstrformat(int);
extern  int getsizformat(int);
extern  int bytrevhost(void);
extern  void sndwrterr(unsigned, unsigned);

static int sreadinew(           /* special handling of sound input       */
    int     infd,               /* to accomodate reads thru pipes & net  */
    char    *inbuf,             /* where nbytes rcvd can be < n requested*/
    int     nbytes,             /*                                       */
    SOUNDINEW *p)               /* extra arg passed for filetyp testing  */
{                               /* on POST-HEADER reads of audio samples */
  int    n, ntot=0;

  do
    if ((n = read(infd, inbuf+ntot, nbytes-ntot)) < 0)
      die("soundfile read error");
  while (n > 0 && (ntot += n) < nbytes);
  if (p->filetyp) {             /* for AIFF and WAV samples */
    if (p->filetyp == TYP_AIFF) {
      if (p->audrem > 0) {      /* AIFF:                  */
        if (ntot > p->audrem)   /*   chk haven't exceeded */
          ntot = p->audrem;     /*   limit of audio data  */
        p->audrem -= ntot;
      }
      else ntot = 0;
    }
    if (ntot && p->bytrev != NULL) /* for post-header of both */
      p->bytrev(inbuf, ntot);   /*   bytrev 2 or 4 as reqd */
  }
  return(ntot);
}


void newsndinset(SOUNDINEW *p)       /* init routine for soundinew   */
{								
/* this is just like sndinset() with sndgetset() inside of it except it
   takes the SOUNDINEW struct, and has to form a fake SOUNDIN struct to
   call readheader() */ 
  int     n;
  HEADATA *hdr;
  long    hdrsize = 0, readlong = 0; /*, framesinbuf, skipframes; -matt */
  char    *sfname, soundiname[128];
  int     sinfd;
  SOUNDIN forReadHeader;

/*????*/
  if (p->fdch.fd != 0)   return;           /* if file already open, rtn  */
  p->channel = ALLCHNLS;                   /* reading all channels       */
  p->analonly = 0;

/************** now the sndgetset() code..... **********/
  if ((n = p->OUTOCOUNT) && n != 1 && n != 2 && n != 4) { /* if appl,chkchnls */
    sprintf(errmsg,"diskin: illegal no of receiving channels");
    goto errtn;
  }
  if (*p->ifilno == sstrcod)    		/* if char string name given */
    strcpy(soundiname,unquote(p->STRARG));      /*     unquote it,  else use */
  else sprintf(soundiname,"soundin.%ld",(long)*p->ifilno);  /* soundin.filno */
  sfname = soundiname;
  if ((sinfd = openin(sfname)) < 0) {           /* open with full dir paths */
    if (isfullpath(sfname))
      sprintf(errmsg,"diskin cannot open %s", sfname);
    else sprintf(errmsg,"diskin can't find \"%s\" in its search paths",
                 sfname);
    goto errtn;
  }
  sfname = retfilnam;                           /* & record fullpath filnam */
  if ((p->format = (short)*p->iformat) > 0)            /* convert spec'd format code */
    p->format |= 0x100;
  p->endfile = 0;
  p->begfile = 0;
  p->filetyp = 0;                      /* initially non-typed for readheader */
        
/******* construct the SOUNDIN struct to use old readheader ***********/
  forReadHeader.filetyp = p->filetyp;
  forReadHeader.bytrev = p->bytrev;
  forReadHeader.audrem = p->audrem;

  if ((hdr=readheader(sinfd,sfname,&forReadHeader)) != NULL /* if headerblk returned */
      && !(readlong = hdr->readlong)) {         /* & hadn't readin audio */
    if (hdr->filetyp == TYP_AIFF                /*    chk the hdr codes  */
        && hdr->aiffdata != NULL
        && hdr->aiffdata->loopmode1 != 0        /* looping aiff:         */
        && (p->analonly || p->OUTOCOUNT))       /*     ok for gen01 only */
      warning("aiff looping file, once through only");
    if (p->analonly) {                          /* anal: if sr param val */
      if (p->sr != 0 && p->sr != hdr->sr) {     /*          use it       */
        sprintf(errmsg,"-s %ld overriding soundfile sr %ld",
                p->sr, hdr->sr);
        warning(errmsg);
        hdr->sr = p->sr;
      }
    }
    else if (hdr->sr != esr) {                  /* non-anal:  cmp w. esr */
      sprintf(errmsg,"%s sr = %ld, orch sr = %7.1f",
              sfname, hdr->sr, esr);
      warning(errmsg);
    }
    if (p->OUTOCOUNT) {                         /* for orch SOUNDIN: */
      if (hdr->nchnls != p->OUTOCOUNT) {        /*        chk nchnls */
        sprintf(errmsg,"%s nchnls = %d, soundin reading as if nchnls = %d",
                sfname, (int) hdr->nchnls, (int) p->OUTOCOUNT);
        warning(errmsg);
        hdr->nchnls = p->OUTOCOUNT;
      }
    }                                            /* else chk sufficient */
    else if (p->channel != ALLCHNLS && p->channel > hdr->nchnls) {
      sprintf(errmsg,"req chan %d, file %s has only %ld",
              p->channel, sfname, hdr->nchnls);
      die(errmsg);
    }
    if (p->format && hdr->format != p->format) {
      sprintf(errmsg,"diskin %s superceded by %s header format %s",
              getstrformat((int)p->format), sfname,
              getstrformat((int)hdr->format));
      warning(errmsg);
    }
    switch ((p->format = (short)hdr->format)) {   /*    & copy header data */
    case AE_CHAR:   break;
    case AE_UNCH:   break;
    case AE_SHORT:  break;
    case AE_LONG:   break;
    case AE_FLOAT:  break;
    default: sprintf(errmsg,"%s format %s not yet supported",
                     sfname, getstrformat((int)p->format));
    goto errcls;
    }
    p->sampframsiz = (short)(hdr->sampsize * hdr->nchnls);
    hdrsize = hdr->hdrsize;
    p->filetyp = hdr->filetyp;  		/* copy type from headata */
    p->aiffdata = hdr->aiffdata;
    p->sr = (short)hdr->sr;
    p->nchnls = (short)hdr->nchnls;
  }
  else {                                  /* no hdr:  find info elsewhere */
    if (p->analonly) {
      if (!p->sr) {              
        p->sr = (long)DFLT_SR;
        sprintf(errmsg,
                "no -s and no soundheader, using sr default %ld",p->sr);
        warning(errmsg);
      }
    }
    else {
      warning("no soundin header, presuming orchestra sr");
      p->sr = (long) esr;
    }
    if (!p->OUTOCOUNT && p->channel == ALLCHNLS)
      p->channel = 1;
    if (!p->format) {                 /* no format:                     */
      if (p->analonly)                /*     analonly defaults to short */
        p->format = AE_SHORT;
      else p->format = O.outformat;   /*     orch defaults to outformat */
    }
    sprintf(errmsg,"%s has no soundfile header, reading as %s, %d chnl%s",
            sfname, getstrformat((int)p->format), (int)p->channel,
            p->channel == 1 ? "" : "s");
    warning(errmsg);
    p->sampframsiz = getsizformat((int)p->format) * p->channel;
    p->filetyp = 0;                     /*  in_type can't be AIFF or WAV */
    p->aiffdata = NULL;
    p->nchnls = p->channel;
  }
  printf("audio sr = %ld, ", p->sr);
  if (p->nchnls == 1)
    printf("monaural\n");
  else {
    printf("%s, reading ", p->nchnls == 2 ? "stereo" : "quad");
    if (p->channel == ALLCHNLS)
      printf("%s channels\n", p->nchnls == 2 ? "both" : "all");
    else printf("channel %d\n", p->channel);
  }

  if (p->filetyp == TYP_AIFF && bytrevhost() ||
      p->filetyp == TYP_AIFC && bytrevhost() ||
      p->filetyp == TYP_WAV && !bytrevhost()) {
    if (p->format == AE_SHORT)          /* if audio_in needs byte rev   */
      p->bytrev = bytrev2;            /*     set on sample size       */
    else if (p->format == AE_LONG)
      p->bytrev = bytrev4;
    else p->bytrev = NULL;
    printf("opening %s infile %s, with%s bytrev\n",
           p->filetyp == TYP_AIFF ? "AIFF" : TYP_AIFC ? "AIFC" : "WAV",
           sfname, p->bytrev == NULL ? " no" : "");
  }
  else p->bytrev = NULL;
  if (p->sampframsiz <= 0)                           /* must know framsiz */
    die("illegal sampframsiz");
  if (hdr->audsize > 0 )                          /* given audiosize   */
    p->audrem = p->audsize = hdr->audsize;
  else 
    p->audrem = -1;                                /* else mark unknown */

/*****   this next section looked kinda iffy, so rewrote it*********-matt*/
  {
    long nbytes = (long)(*p->iskptim * p->sr) * p->sampframsiz;
    
 					/* The line below changed for 
					 * compatibility with glibc.  
					 * Previously "tell(sinfd)"
				 	 */ 

    p->firstsampinfile = lseek(sinfd, 0, SEEK_CUR); /* this might not work for all header types??? */
            	 	
    if ((p->audrem > 0) && (nbytes > p->audrem)) {
      warning("skip time larger than audio data,substituting zero.");
      nbytes = 0;
    }
    if (nbytes > 0) {
      if (hdr->audsize > 0 ) 	/* change audsize   */
        p->audrem = p->audsize = p->audsize-nbytes+p->firstsampinfile;
	 		       	
      if ((p->filepos = lseek(sinfd, nbytes+p->firstsampinfile, 0)) < 0)  /* seek to bndry */
        die("diskin seek error: invalid skip time");
    }
    else {
      p->begfile = TRUE;
      if (*p->ktransp < 0) p->endfile = TRUE;
    }
	 	    
    if ((n = sreadinew(sinfd,p->inbuf,SNDINEWBUFSIZ,p)) == 0) /* now rd fulbuf */
      p->endfile = 1;
    p->inbufp = p->inbuf;
    p->bufend = p->inbuf + n;
  }
  /***********************************************************************/
  if (sinfd > 0) {
    /* if soundinset successful   */
    p->fdch.fd = sinfd;              /*     store & log the fd     */
    fdrecord(&p->fdch);              /*     instr will close later */          
    p->guardpt = p->bufend - p->sampframsiz;
    p->phs = 0.;                
    return;
  }
  else initerror(errmsg); 
  return;
        
 errcls: close(sinfd);               /* init error:  close any open file */
 errtn:  return;                  /*              return empty handed */
}                

void soundinew(SOUNDINEW *p)    /*  a-rate routine for soundinew */
{
  float 	*r1, *r2, *r3, *r4, ktransp,looping;
  int 		chnsout, n, ntogo, bytesLeft;	
  double	phs,phsFract,phsTrunc;
  char		*inbufp = p->inbufp;
	
  if ((!p->bufend) || (!p->inbufp) || (!p->sampframsiz)) {
    initerror("diskin: not initialised");
    return;
  }
  r1 = 		p->r1;
  r2 = 		p->r2;
  r3 = 		p->r3;
  r4 = 		p->r4;
  ktransp = *p->ktransp;
  looping = *p->ilooping;
  chnsout = p->OUTOCOUNT;
  phs = 	p->phs;
  ntogo = 	ksmps;

  if (ktransp > 0 ) {	/* forwards... */
    if (phs < 0) phs = 0; /* we have just switched directions, forget (negative) old phase */
    if (p->endfile) {
      if (p->begfile) p->endfile = FALSE;
      else goto filend;
    }

    while (ntogo) {
      switch (chnsout) {     	/* a lot of the following code has been "written out" for speed */
      case 1:
        phsFract = modf(phs,&phsTrunc);
        switch (p->format) {
        case AE_CHAR:		              		
          do {
            *r1++ = (float) (*(char *)inbufp +
                             (*(char *)(inbufp + 1) -
                              *(char *)inbufp) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc);
            --ntogo;
          } 
          while ((inbufp < p->guardpt) && (ntogo));
          break;
        case AE_UNCH:		              		
          do {
            *r1++ = (float) (*(unsigned char *)inbufp +
                             (*(unsigned char *)(inbufp + 1) -
                              *(unsigned char *)inbufp) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc);
            --ntogo;
          } 
          while ((inbufp < p->guardpt) && (ntogo));
          break;
        case AE_SHORT:
          do {
            *r1++ = (float) (*(short *)inbufp +
                             (*(short *)(inbufp + 2) -
                              *(short *)inbufp) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 2);
            --ntogo;
          } 
          while ((inbufp < p->guardpt) && (ntogo));
          break;
        case AE_LONG:
          do {
            *r1++ = (float) (*(long *)inbufp + (*(long *)(inbufp + 4) - *(long *)inbufp) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 4);
            --ntogo;
          } 
          while ((inbufp < p->guardpt) && (ntogo));
          break;
        case AE_FLOAT:
          do {
            *r1++ = (float) (*(float *)inbufp + (*(float *)(inbufp + 4) - *(float *)inbufp) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 4);
            --ntogo;
          } 
          while ((inbufp < p->guardpt) && (ntogo));
          break;
        }
        break;
      case 2:			               
        phsFract = modf(phs,&phsTrunc);
        switch (p->format) {			              
        case AE_CHAR:
          do {
            *r1++ = (float) (*(char *)inbufp +
                             (*(char *)(inbufp + 2) -
                              *(char *)inbufp) * phsFract);
            *r2++ = (float) (*(char *)(inbufp + 1) +
                             (*(char *)(inbufp + 3) -
                              *(char *)(inbufp + 1)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 2);
            --ntogo;
          } 
          while ((inbufp < p->guardpt) && (ntogo));
          break;
        case AE_UNCH:
          do {
            *r1++ = (float) (*(unsigned char *)inbufp +
                             (*(unsigned char *)(inbufp + 2) -
                              *(unsigned char *)inbufp) * phsFract);
            *r2++ = (float) (*(unsigned char *)(inbufp + 1) +
                             (*(unsigned char *)(inbufp + 3) -
                              *(unsigned char *)(inbufp + 1)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 2);
            --ntogo;
          } 
          while ((inbufp < p->guardpt) && (ntogo));
          break;
        case AE_SHORT:
          do {
            *r1++ = (float) (*(short *)inbufp +
                             (*(short *)(inbufp + 4) -
                              *(short *)inbufp) * phsFract);
            *r2++ = (float) (*(short *)(inbufp + 2) +
                             (*(short *)(inbufp + 6) -
                              *(short *)(inbufp + 2)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 4);
            --ntogo;
          } 
          while ((inbufp < p->guardpt) && (ntogo));
          break;
        case AE_LONG:
          do {
            *r1++ = (float) (*(long *)inbufp +
                             (*(long *)(inbufp + 8) -
                              *(long *)inbufp) * phsFract);
            *r2++ = (float) (*(long *)(inbufp + 4) +
                             (*(long *)(inbufp + 12) -
                              *(long *)(inbufp + 4)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 8);
            --ntogo;
          } 
          while ((inbufp < p->guardpt) && (ntogo));
          break;
        case AE_FLOAT:
          do {
            *r1++ = (float) (*(float *)inbufp +
                             (*(float *)(inbufp + 8) -
                              *(float *)inbufp) * phsFract);
            *r2++ = (float) (*(float *)(inbufp + 4) +
                             (*(float *)(inbufp + 12) -
                              *(float *)(inbufp + 4)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 8);
            --ntogo;
          } 
          while ((inbufp < p->guardpt) && (ntogo));
          break;
        }
        break;
      case 4:
        phsFract = modf(phs,&phsTrunc);
        switch (p->format) {
        case AE_CHAR:
          do {
            *r1++ = (float) (*(char *)inbufp +
                             (*(char *)(inbufp + 4) -
                              *(char *)inbufp) * phsFract);
            *r2++ = (float) (*(char *)(inbufp + 1) +
                             (*(char *)(inbufp + 5) -
                              *(char *)(inbufp + 1)) * phsFract);
            *r3++ = (float) (*(char *)(inbufp + 2) +
                             (*(char *)(inbufp + 6) -
                              *(char *)(inbufp + 2)) * phsFract);
            *r4++ = (float) (*(char *)(inbufp + 3) +
                             (*(char *)(inbufp + 7) -
                              *(char *)(inbufp + 3)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 4);
            --ntogo;
          } 
          while ((inbufp < p->guardpt) && (ntogo));
          break;
        case AE_UNCH:
          do {
            *r1++ = (float) (*(unsigned char *)inbufp +
                             (*(unsigned char *)(inbufp + 4) -
                              *(unsigned char *)inbufp) * phsFract);
            *r2++ = (float) (*(unsigned char *)(inbufp + 1) +
                             (*(unsigned char *)(inbufp + 5) -
                              *(unsigned char *)(inbufp + 1)) * phsFract);
            *r3++ = (float) (*(unsigned char *)(inbufp + 2) +
                             (*(unsigned char *)(inbufp + 6) -
                              *(unsigned char *)(inbufp + 2)) * phsFract);
            *r4++ = (float) (*(unsigned char *)(inbufp + 3) +
                             (*(unsigned char *)(inbufp + 7) -
                              *(unsigned char *)(inbufp + 3)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 4);
            --ntogo;
          } 
          while ((inbufp < p->guardpt) && (ntogo));
          break;
        case AE_SHORT:
          do {
            *r1++ = (float) (*(short *)inbufp +
                             (*(short *)(inbufp + 8) -
                              *(short *)inbufp) * phsFract);
            *r2++ = (float) (*(short *)(inbufp + 2) +
                             (*(short *)(inbufp + 10) -
                              *(short *)(inbufp + 2)) * phsFract);
            *r3++ = (float) (*(short *)(inbufp + 4) +
                             (*(short *)(inbufp + 12) -
                              *(short *)(inbufp + 4)) * phsFract);
            *r4++ = (float) (*(short *)(inbufp + 6) +
                             (*(short *)(inbufp + 14) -
                              *(short *)(inbufp + 6)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 8);
            --ntogo;
          } 
          while ((inbufp < p->guardpt) && (ntogo));
          break;
        case AE_LONG:
          do {
            *r1++ = (float) (*(long *)inbufp +
                             (*(long *)(inbufp + 16) -
                              *(long *)inbufp) * phsFract);
            *r2++ = (float) (*(long *)(inbufp + 4) +
                             (*(long *)(inbufp + 20) -
                              *(long *)(inbufp + 4)) * phsFract);
            *r3++ = (float) (*(long *)(inbufp + 8) +
                             (*(long *)(inbufp + 24) -
                              *(long *)(inbufp + 8)) * phsFract);
            *r4++ = (float) (*(long *)(inbufp + 12) +
                             (*(long *)(inbufp + 28) -
                              *(long *)(inbufp + 12)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 16);
            --ntogo;
          } 
          while ((inbufp < p->guardpt) && (ntogo));
          break;
        case AE_FLOAT:
          do {
            *r1++ = (float) (*(float *)inbufp +
                             (*(float *)(inbufp + 16) -
                              *(float *)inbufp) * phsFract);
            *r2++ = (float) (*(float *)(inbufp + 4) +
                             (*(float *)(inbufp + 20) -
                              *(float *)(inbufp + 4)) * phsFract);
            *r3++ = (float) (*(float *)(inbufp + 8) +
                             (*(float *)(inbufp + 24) -
                              *(float *)(inbufp + 8)) * phsFract);
            *r4++ = (float) (*(float *)(inbufp + 12) +
                             (*(float *)(inbufp + 28) -
                              *(float *)(inbufp + 12)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 16);
            --ntogo;
          } 
          while ((inbufp < p->guardpt) && (ntogo));
          break;
        }
        break;
      }
			
      bytesLeft = (int)(p->bufend - inbufp);
      if (bytesLeft <= p->sampframsiz) {	/* first set file position to where inbuf p "thinks" its pointing to */
        p->filepos = lseek(p->fdch.fd,-bytesLeft,1);
        if ((n = sreadinew(p->fdch.fd,p->inbuf,SNDINEWBUFSIZ,p)) == 0) {
          if (looping) {
            /* go to beginning of file.
               depending on the pitch and
               phase, we might drop a few "guardpoint" samples, but
               this ugen is intended for large files anyway -- if a
               few end samples are critical for looping, use oscil or
               table!!!!  */
            p->audrem = p->audsize;
            p->filepos = lseek(p->fdch.fd,p->firstsampinfile,0);
            if ((n = sreadinew(p->fdch.fd,p->inbuf,SNDINEWBUFSIZ,p)) == 0)
              die("error trying to loop back to the beginning of the sound file!?!??");
            p->begfile = 1;
            phs = 0;
            inbufp = p->inbufp = p->inbuf;
            p->bufend = p->inbuf + n;
            p->guardpt = p->bufend - p->sampframsiz;
          }
          else {
            p->endfile = TRUE;
            goto filend;
          }
        }
        else {
          inbufp = p->inbufp = p->inbuf;
          p->bufend = p->inbuf + n;
          p->guardpt = p->bufend - p->sampframsiz;
          phs = modf(phs,&phsTrunc);
          p->begfile = FALSE;
        }
      }
    }
  }
	
  else {	/* backwards...			same thing but different */
    if (phs > 0) phs = 0; /* we have just switched directions, forget (positive) old phase */
 		
    if (p->endfile) { /* firewall-flag signaling when we are at either end of the file */
      if (p->begfile) goto filend; 	/* make sure we are at beginning, not end */
      else p->endfile = FALSE; 		/* at end, must have just switched directions, start making sound again */
    }
			
    while (ntogo) {	
      switch(chnsout) {
      case 1:
        phsFract = modf(phs,&phsTrunc); /* phsFract and phsTrunc will be non-positive */
        switch (p->format) {
        case AE_CHAR:
          do {
            *r1++ = (float) (*(char *)inbufp +
                             (*(char *)inbufp -
                              *(char *)(inbufp - 1)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc);
            --ntogo;
          } 
          while ((inbufp > p->inbuf) && (ntogo));
          /* make sure it
             points to the second sample or greater in the buffer,
             because we need at least two to interpolate */
          break;
        case AE_UNCH:
          do {
            *r1++ = (float) (*(unsigned char *)inbufp +
                             (*(unsigned char *)inbufp -
                              *(unsigned char *)(inbufp - 1)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc);
            --ntogo;
          } 
          while ((inbufp > p->inbuf) && (ntogo));			       
          break;
        case AE_SHORT:
          do {
            *r1++ = (float) (*(short *)inbufp +
                             (*(short *)inbufp -
                              *(short *)(inbufp - 2)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 2);
            --ntogo;
          } 
          while ((inbufp > p->inbuf) && (ntogo));
          break;
        case AE_LONG:
          do {
            *r1++ = (float) (*(long *)inbufp +
                             (*(long *)inbufp -
                              *(long *)(inbufp - 4)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 4);
            --ntogo;
          } 
          while ((inbufp > p->inbuf) && (ntogo));	
          break;
        case AE_FLOAT:
          do {
            *r1++ = (float) (*(float *)inbufp +
                             (*(float *)inbufp -
                              *(float *)(inbufp - 4)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 4);
            --ntogo;
          } 
          while ((inbufp > p->inbuf) && (ntogo));	
          break;
        }
        break;
      case 2:
        phsFract = modf(phs,&phsTrunc);	/*  phsFract will be negative */
        switch (p->format) {			               
        case AE_CHAR:
          do {
            *r1++ = (float) (*(char *)inbufp +
                             (*(char *)inbufp -
                              *(char *)(inbufp - 2)) * phsFract);
            *r2++ = (float) (*(char *)(inbufp + 1) +
                             (*(char *)(inbufp + 1) -
                              *(char *)(inbufp - 1)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 2);
            --ntogo;
          } 
          while ((inbufp > p->inbuf) && (ntogo));
          break;
        case AE_UNCH:
          do {
            *r1++ = (float) (*(unsigned char *)inbufp +
                             (*(unsigned char *)inbufp -
                              *(unsigned char *)(inbufp - 2)) * phsFract);
            *r2++ = (float) (*(unsigned char *)(inbufp +
1) + (*(unsigned char *)(inbufp + 1) - *(unsigned char *)(inbufp - 1)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 2);
            --ntogo;
          } 
          while ((inbufp > p->inbuf) && (ntogo));
          break;
        case AE_SHORT:
          do {
            *r1++ = (float) (*(short *)inbufp +
                             (*(short *)inbufp -
                              *(short *)(inbufp - 4)) * phsFract);
            *r2++ = (float) (*(short *)(inbufp + 2) +
                             (*(short *)(inbufp + 2) -
                              *(short *)(inbufp - 2)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 4);
            --ntogo;
          } 
          while ((inbufp > p->inbuf) && (ntogo));
          break;
        case AE_LONG:
          do {
            *r1++ = (float) (*(long *)inbufp +
                             (*(long *)inbufp -
                              *(long *)(inbufp - 8)) * phsFract);
            *r2++ = (float) (*(long *)(inbufp + 4) +
                             (*(long *)(inbufp + 4) -
                              *(long *)(inbufp - 4)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 8);
            --ntogo;
          } 
          while ((inbufp > p->inbuf) && (ntogo));
          break;
        case AE_FLOAT:
          do {
            *r1++ = (float) (*(float *)inbufp +
                             (*(float *)inbufp -
                              *(float *)(inbufp - 8)) * phsFract);
            *r2++ = (float) (*(float *)(inbufp + 4) +
                             (*(float *)(inbufp + 4) -
                              *(float *)(inbufp - 4)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 8);
            --ntogo;
          } 
          while ((inbufp > p->inbuf) && (ntogo));
          break;
        }
        break;
      case 4:
        phsFract = modf(phs,&phsTrunc);	/*  phsFract will be negative */
        switch (p->format) {
        case AE_CHAR:
          do {
            *r1++ = (float) (*(char *)inbufp +
                             (*(char *)inbufp -
                              *(char *)(inbufp - 4)) * phsFract);
            *r2++ = (float) (*(char *)(inbufp + 1) +
                             (*(char *)(inbufp + 1) -
                              *(char *)(inbufp - 3)) * phsFract);
            *r3++ = (float) (*(char *)(inbufp + 2) +
                             (*(char *)(inbufp + 2) -
                              *(char *)(inbufp - 2)) * phsFract);
            *r4++ = (float) (*(char *)(inbufp + 3) +
                             (*(char *)(inbufp + 3) -
                              *(char *)(inbufp - 1)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 4);
            --ntogo;
          } 
          while ((inbufp > p->inbuf) && (ntogo));
          break;
        case AE_UNCH:
          do {
            *r1++ = (float) (*(unsigned char *)inbufp +
                             (*(unsigned char *)inbufp -
                              *(unsigned char *)(inbufp - 4)) * phsFract);
            *r2++ = (float) (*(unsigned char *)(inbufp + 1) +
                             (*(unsigned char *)(inbufp + 1) -
                              *(unsigned char *)(inbufp - 3)) * phsFract);
            *r3++ = (float) (*(unsigned char *)(inbufp + 2) +
                             (*(unsigned char *)(inbufp + 2) -
                              *(unsigned char *)(inbufp - 2)) * phsFract);
            *r4++ = (float) (*(unsigned char *)(inbufp + 3) +
                             (*(unsigned char *)(inbufp + 3) -
                              *(unsigned char *)(inbufp - 1)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 4);
            --ntogo;
          } 
          while ((inbufp > p->inbuf) && (ntogo));
          break;
        case AE_SHORT:
          do {
            *r1++ = (float) (*(short *)inbufp +
(*(short *)inbufp - *(short *)(inbufp - 8)) * phsFract);
            *r2++ = (float) (*(short *)(inbufp + 2) +
                             (*(short *)(inbufp + 2) -
                              *(short *)(inbufp - 6)) * phsFract);
            *r3++ = (float) (*(short *)(inbufp + 4) +
                             (*(short *)(inbufp + 4) -
                              *(short *)(inbufp - 4)) * phsFract);
            *r4++ = (float) (*(short *)(inbufp + 6) +
                             (*(short *)(inbufp + 6) -
                              *(short *)(inbufp - 2)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 8);
            --ntogo;
          } 
          while ((inbufp > p->inbuf) && (ntogo));
          break;
        case AE_LONG:
          do {
            *r1++ = (float) (*(long *)inbufp +
                             (*(long *)inbufp -
                              *(long *)(inbufp - 16)) * phsFract);
            *r2++ = (float) (*(long *)(inbufp + 4) +
                             (*(long *)(inbufp + 4) -
                              *(long *)(inbufp - 12)) * phsFract);
            *r3++ = (float) (*(long *)(inbufp + 8) +
                             (*(long *)(inbufp + 8) -
                              *(long *)(inbufp - 8)) * phsFract);
            *r4++ = (float) (*(long *)(inbufp + 12) +
                             (*(long *)(inbufp + 12) -
                              *(long *)(inbufp - 4)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 16);
            --ntogo;
          } 
          while ((inbufp > p->inbuf) && (ntogo));
          break;
        case AE_FLOAT:
          do {
            *r1++ = (float) (*(float *)inbufp +
                             (*(float *)inbufp -
                              *(float *)(inbufp - 16)) * phsFract);
            *r2++ = (float) (*(float *)(inbufp + 4) +
                             (*(float *)(inbufp + 4) -
                              *(float *)(inbufp - 12)) * phsFract);
            *r3++ = (float) (*(float *)(inbufp + 8) +
                             (*(float *)(inbufp + 8) -
                              *(float *)(inbufp - 8)) * phsFract);
            *r4++ = (float) (*(float *)(inbufp + 12) +
                             (*(float *)(inbufp + 12) -
                              *(float *)(inbufp - 4)) * phsFract);
            phs += ktransp;
            phsFract = modf(phs,&phsTrunc);
            inbufp = p->inbufp + (long)(phsTrunc * 16);
            --ntogo;
          } 
          while ((inbufp > p->inbuf) && (ntogo));
          break;
        }
        break;
      }

      if (inbufp <= p->inbuf) { /* we need to get some more samples!! */
        if (p->begfile) {
          if (looping) {	/* hopes this works -- set 1 buffer lenght at end of sound file */
            p->filepos = lseek(p->fdch.fd,
                               p->firstsampinfile+p->audsize-SNDINEWBUFSIZ,
                               0);
            phs = -0.;
            p->begfile = 0;
          }
          else {
            p->endfile = 1;
            goto filend;
          }
        }
        else {							
          bytesLeft = (int)(inbufp - p->inbuf + p->sampframsiz);
                    /* we're going backwards, so bytesLeft should be
                     * non-positive because inpufp should be pointing
                     * to the first sample in the buffer or "in front"
                     * the buffer.  But we must add a sample frame
                     * (p->ampframsiz) to make sure the sample we are
                     * pointing at right now becomes the last sample
                     * in the next buffer*/
          if ((p->filepos = lseek(p->fdch.fd,
                                  (bytesLeft-SNDINEWBUFSIZ-SNDINEWBUFSIZ),
                                  1)) <= p->firstsampinfile) {
            p->filepos = lseek(p->fdch.fd,p->firstsampinfile,0);
            p->begfile = 1;
          }
        }
		        	
        p->audrem = p->audsize;	/* a hack to prevent errors (returning 'ntot')in the sread for AIFF */
        	
        if ((n = sreadinew(p->fdch.fd,p->inbuf,SNDINEWBUFSIZ,p)) !=
            SNDINEWBUFSIZ) { 	/* we should never get here. if we do,
            we're fucked because didn't get a full buffer and our
            present sample is the last sample of the buffer!!!  */
          die("diskin read error - during backwards playback");
          return;
        }
                                /* now get the correct remaining size */
        p->audrem = p->audsize - p->firstsampinfile - p->filepos;
        p->bufend = p->inbuf + n;
                                /* point to the last sample in buffer */
        inbufp = p->inbufp = p->guardpt = p->bufend - p->sampframsiz;
        phs = modf(phs,&phsTrunc);
      }
    }
  }

  p->inbufp = inbufp;
  p->phs = modf(phs,&phsTrunc);

  return;

 filend:
  switch(chnsout) {                   /* if past end of file, */
  case 1:
    do *r1++ = 0.0f;               /*    move in zeros     */
    while (--ntogo);
    break;
  case 2:
    do {
      *r1++ = 0.0f;
      *r2++ = 0.0f;
    } while (--ntogo);
    break;
  case 4:
    do {
      *r1++ = 0.0f;
      *r2++ = 0.0f;
      *r3++ = 0.0f;
      *r4++ = 0.0f;
    } while (--ntogo);
  }
}

/* Empty buffer to file */
static void sndwrt1(int fd, float *buf, int nsamps)  /* diskfile write */
{
    int n;
    char buffer[SNDOUTSMPS];
    for (n=0; n<SNDOUTSMPS; n++) {
      long x = (long)buf[n];
      if (x > 32767) x = 32767;
      else if (x < -32768) x = 32768;
      buffer[n] = (char)(x >> 8);
    }
    if ((n = write(fd, (char*)buffer, nsamps)) < nsamps)
      sndwrterr(n, nsamps);
}

static void sndwrtu(int fd, float *buf, int nsamps)  /* diskfile write */
{
    int n;
    unsigned char buffer[SNDOUTSMPS];
    for (n=0; n<SNDOUTSMPS; n++) {
      long x = (long)buf[n];
      if (x > 32767) x = 32767;
      else if (x < -32768) x = 32768;
      buffer[n] = (unsigned char)(x >> 8)^0x80;
    }
    if ((n = write(fd, buffer, nsamps)) < nsamps)
      sndwrterr(n, nsamps);
}

static void sndwrt2rev(int fd, float *buf, int nsamps) /* diskfile write */
{
    int n, nbytes;
    short buffer[SNDOUTSMPS];
    for (n=0; n<SNDOUTSMPS; n++) {
      long x = (long)buf[n];
      if (x > 32767) x = 32767;
      else if (x < -32768) x = 32768;
      buffer[n] = (short)x;
    }
    bytrev2((char *)buffer, nbytes = (nsamps<<1));    /* rev bytes in shorts  */
    if ((n = write(fd, (char*)buffer, nbytes)) < nbytes)
      sndwrterr(n, nbytes);
}

static void sndwrt2(int fd, float *buf, int nsamps) /* diskfile write */
{
    int n, nbytes;
    short buffer[SNDOUTSMPS];
    for (n=0; n<SNDOUTSMPS; n++) {
      long x = (long)buf[n];
      if (x > 32767) x = 32767;
      else if (x < -32768) x = 32768;
      buffer[n] = (short)x;
    }
    nbytes = nsamps << 1;
    if ((n = write(fd, buffer, nbytes)) < nbytes)
      sndwrterr(n, nbytes);
}

static void sndwrt4rev(int fd, float *buf, int nsamps) /* diskfile write */
{
    int n, nbytes;
    long buffer[SNDOUTSMPS];
    for (n=0; n<SNDOUTSMPS; n++) {
      buffer[n] = (long)buf[n];
    }
    nbytes = nsamps << 2;
    bytrev4((char *)buffer, nbytes);    /* rev bytes in longs   */
    if ((n = write(fd, buffer, nbytes)) < nbytes)
      sndwrterr(n, nbytes);
}

static void sndwrt4(int fd, float *buf, int nsamps) /* diskfile write */
{
    int n, nbytes;
    long buffer[SNDOUTSMPS];
    for (n=0; n<SNDOUTSMPS; n++) {
      buffer[n] = (long)buf[n];
    }
    nbytes = nsamps << 2;
    if ((n = write(fd, buffer, nbytes)) < nbytes)
      sndwrterr(n, nbytes);
}

static void sndwrtf(int fd, float *buf, int nsamps) /* diskfile write */
{
    int n, nbytes;
    nbytes = nsamps << 2;
    if ((n = write(fd, buf, nbytes)) < nbytes)
      sndwrterr(n, nbytes);
}

void sndo1set(SNDOUT *p)            /* init routine for instr soundout   */
{                            
    int    soutfd, filno;
    char   *sfname, sndoutname[128];

    if (p->c.fdch.fd != 0)   return;           /* if file already open, rtn  */
    if (*p->c.ifilcod == sstrcod)
      strcpy(sndoutname, unquote(p->STRARG));
    else if ((filno = (int)*p->c.ifilcod) >= 0) /* Positive or negative? */
      sprintf(sndoutname,"soundout.%d", filno);
/*     else { */
/*       char *setname; */
/*       if (strsets != NULL && filno <= STRSMAX */
/*           && (setname = strsets[filno]) != NULL) */
/*         strcpy(sndoutname, setname); */
/*       else { */
/*         sprintf(errmsg,"soundout: no strset %d in orch", filno); */
/*         goto errtn; */
/*       } */
/*     } */
    sfname = sndoutname;
    if ((soutfd = openout(sfname, 1)) < 0) {   /* if openout successful */
      if (isfullpath(sfname))
        sprintf(errmsg,"soundout cannot open %s", sfname);
      else sprintf(errmsg,"soundout can't find %s in search paths", sfname);
      goto errtn;
    }
    sfname = retfilnam;
    if ((p->c.format = (short)*p->c.iformat) > 0)
      p->c.format |= 0x100;

    if (p->c.filetyp == TYP_AIFF && bytrevhost() ||
        p->c.filetyp == TYP_AIFC && bytrevhost() ||
        p->c.filetyp == TYP_WAV && !bytrevhost()) {
      int rev;
      switch(p->c.format) {
      case AE_UNCH:  p->c.swrtmethod = sndwrtu;    rev = 0;   break;
      case AE_CHAR:
      case AE_ALAW:
      case AE_ULAW:  p->c.swrtmethod = sndwrt1;    rev = 0;   break;
      case AE_SHORT: p->c.swrtmethod = sndwrt2rev; rev = 1;   break;
      case AE_LONG:  p->c.swrtmethod = sndwrt4rev; rev = 1;   break;
      case AE_FLOAT: p->c.swrtmethod = sndwrtf;    rev = 0;   break;
      }
      printf("opening %s outfile %s, with%s bytrev\n",
             p->c.filetyp==TYP_AIFF ? "AIFF":
             p->c.filetyp==TYP_AIFC ? "AIFC":"WAV", sfname, rev ? "":" no");
    }
    else switch(p->c.format) {
    case AE_UNCH:  p->c.swrtmethod = sndwrt1;  break;
    case AE_CHAR:
    case AE_ALAW:
    case AE_ULAW:  p->c.swrtmethod = sndwrt1;  break;
    case AE_SHORT: p->c.swrtmethod = sndwrt2;  break;
    case AE_LONG:  p->c.swrtmethod = sndwrt4;  break;
    case AE_FLOAT: p->c.swrtmethod = sndwrtf;  break;
    }
    p->c.outbufp = p->c.outbuf;		/* fix - isro 20-11-96 */
    p->c.bufend = p->c.outbuf + SNDOUTSMPS; /* fix - isro 20-11-96 */
    p->c.fdch.fd = soutfd;                  /*     store & log the fd     */
    fdrecord(&p->c.fdch);                   /*     instr will close later */
    return;
 errtn:
    initerror(errmsg);                      /* else just print the errmsg */
}

void soundout(SNDOUT *p)
{
    float  *outbufp, *asig;
    int    nn, nsamps, ospace;

    asig = p->asig;
    outbufp = p->c.outbufp;
    nsamps = ksmps;
    ospace = (p->c.bufend - outbufp);
 nchk:
    if ((nn = nsamps) > ospace)
      nn = ospace;
    nsamps -= nn;
    ospace -= nn;
    do  *outbufp++ = *asig++;
    while (--nn);
    if (!ospace) {              /* when buf is full  */
      p->c.swrtmethod(p->c.fdch.fd, (void *)p->c.outbuf, p->c.bufend - p->c.outbuf); 
      outbufp = p->c.outbuf;                      
      ospace = SNDOUTSMPS;
      if (nsamps) goto nchk;    /*   chk rem samples */
    }
    p->c.outbufp = outbufp;
}

void sndo2set(SNDOUTS *p)
{}

void soundouts(SNDOUTS *p)
{}

