ftp.nice.ch/pub/next/unix/music/clm.d.tar.gz#/headers.c

This is headers.c in view mode; [Download] [Up]

/* readers/writers for various sound file headers
 *                                               
 *   In the header, CLM needs support for an arbitrarily long comment (for mix et al),   
 *   and in general I'm only interested in writing 2's complement, 16-bit (or better), 
 *   uncompressed sound files with one sound per file and (preferably) channels interleaved.
 *   "Linear" below means 2's complement integer.
 *
 * Currently supported read/write:                         
 *      NeXT/Sun/DEC 16-bit linear (format 3), with AFsp extensions to the header
 *      AIFF 16-bit linear
 *      RIFF 16-bit linear (called PCM, which strikes me as confused)
 *
 * Currently supported read-only:
 *      NeXT/Sun/DEC 8-bit mulaw, 8-bit linear, 24-bit linear, 32-bit linear, 32-bit float, 8-bit alaw, 16-bit emphasized, double
 *      AIFF 8-bit linear, 8SVX 8-bit linear
 *      IRCAM 16-bit linear (BICSF), EBICSF
 *      NIST-SPHERE 16-bit linear
 *      INRS, ESPS, AVR 16-bit linear, AVR 8-bit linear and unsigned
 *      RIFF 8-bit alaw, 8-bit mulaw, 8-bit unsigned, 32-bit linear
 *      VOC 8-bit signed(?)
 *      no header (will prompt for info it needs) (CD-R is one of these)
 *      Sound Tools 8-bit unsigned(?)
 *      Turtle Beach SMP 16-bit linear
 *      Sound Designer II (the data file is the same as a no-header file with interleaved big-endian data)
 *
 * for a few of these I'm still trying to get documentation -- best sources of info
 * are ftp.cwi.nl:pub/audio (info files), the AFsp sources, the SOX sources, and the CSound sources.
 * sox and gsm are at ftp.cwi.nl, AFsp is from kabal@Polaris.EE.McGill.CA (Peter Kabal) as
 * aldebaran.EE.McGill.CA:pub/AFsp/AFspV1R2.tar.Z. The Sound Designer formats are described
 * in the "Developer Documentation" from Digidesign.
 *
 * I'm willing to add almost anything to this list -- to add another header
 * I need the header layout as a sequence of bytes; to add another data format
 * I need the interpretation of that data.  To write a new header type, there
 * has to be room for any amount of commentary in the header.
 *
 * Unsupported with no plans for eventual support:
 *      Sun/NeXT endless compressed versions, AES and MULAW-SQUELCH formats
 *      AIFC compressed, HCOM, MOD, Mus10, SAM
 *      RIFF ADPCM
 *      Sound Designer format 1 is obsolete
 */

#include <stdio.h>
#include <fcntl.h>
#include <math.h>
#include "sound_types.h"

#ifndef AKCL
extern int clm_read(int fd, char *buf, int n);
extern int clm_write(int fd, char *buf, int n);
extern short clm_short(short n);
extern int clm_int(int n);
extern long clm_seek(int fd, long offset, int origin);
extern int clm_reopen_write(char *arg);
extern int clm_open_read(char *arg); 
extern int clm_create(char *name);
#endif

#define HDRBUFSIZ 256
                  /* better be more than 50 */
static char hdrbuf[HDRBUFSIZ];

static char I_DSND[4] = {'.','s','n','d'}; /* NeXT/Sun/Dec/SGI/AFsp first word */
static char I_FORM[4] = {'F','O','R','M'}; /* AIFF first word */
static char I_AIFF[4] = {'A','I','F','F'}; /* AIFF second word */
static char I_AIFC[4] = {'A','I','F','C'}; /* ditto but might be compressed data */
static char I_COMM[4] = {'C','O','M','M'};
static char I_SSND[4] = {'S','S','N','D'};
static char I_APPL[4] = {'A','P','P','L'};
static char I_CLM_[4] = {'C','L','M',' '};  /* I hereby claim this AIFF chunk name */
static char I_RIFF[4] = {'R','I','F','F'};  /* RIFF first word */
static char I_WAVE[4] = {'W','A','V','E'};
static char I_fmt_[4] = {'f','m','t',' '};
static char I_data[4] = {'d','a','t','a'};
static char I_clm_[4] = {'c','l','m',' '};
static char I_NIST[4] = {'N','I','S','T'}; /* first word of NIST SPHERE files */
static char I_8SVX[4] = {'8','S','V','X'}; /* AIFF other choice */
static char I_VOC0[4] = {'C','r','e','a'}; /* Actual text is "Creative Voice File" */
static char I_VOC1[4] = {'t','i','v','e'};
static char I_SNDT[4] = {'S','O','U','N'}; /* Sound Tools first word="SOUND" -- not unique as SMP files start with "SOUND SAMPLE" */
static char I_SMP1[4] = {'D',' ','S','A'};
static char I_SMP2[4] = {'M','P','L','E'};
static char I_BODY[4] = {'B','O','D','Y'}; /* next 4 for 8svx chunk names */
static char I_VHDR[4] = {'V','H','D','R'};
static char I_CHAN[4] = {'C','H','A','N'};
static char I_ANNO[4] = {'A','N','N','O'};
static char I_AVR_[4] = {'2','B','I','T'}; /* first word of AVR files */
static int I_DECN = 0x2e736400;            /* first word of DEC files (?) */
static int I_IRCAM = 0x0001a364;           /* ditto IRCAM */

#define NINRS	7
static const unsigned int I_INRS[NINRS]={0xcb460020,0xd0465555,0xfa460000,0x1c470040,0xc5b8ff7f,0x7a470000,0x9c470040};


static int data_size, data_location, srate, chans, header_type, sound_format, original_sound_format;
static int comment_start, comment_end, header_distributed, type_specifier;

int c_snd_header_data_size (void) {return(data_size);}
int c_snd_header_data_location (void) {return(data_location);}
int c_snd_header_chans (void) {return(chans);}
int c_snd_header_srate (void) {return(srate);}
int c_snd_header_type (void) {return(header_type);}
int c_snd_header_format (void) {return(sound_format);}
int c_snd_header_distributed (void) {return(header_distributed);}
int c_snd_header_comment_start (void) {return(comment_start);}
int c_snd_header_comment_end (void) {return(comment_end);}
int c_snd_header_type_specifier (void) {return(type_specifier);}
int c_snd_header_original_format (void) {return(original_sound_format);}
void c_set_snd_header (int in_srate, int in_chans, int in_format) {srate=in_srate; chans=in_chans; sound_format=in_format;}

int c_snd_header_datum_size (void)
{
  switch (sound_format)
    {
    case snd_8_linear: return(1); break;
    case snd_16_linear: return(2); break;
    case snd_8_unsigned: return(1); break;
    case snd_8_mulaw: return(1); break;
    case snd_8_alaw: return(1); break;
    case snd_32_linear: return(4); break;
    case snd_32_float: return(4); break;
    case snd_24_linear: return(3); break;
    case snd_64_double: return(8); break;
    }
}

int c_snd_samples (int format, int size)
{
  switch (format)
    {
    case snd_8_linear: return(size<<1); break;
    case snd_16_linear: return(size); break;
    case snd_8_unsigned: return(size<<1); break;
    case snd_8_mulaw: return(size<<1); break;
    case snd_8_alaw: return(size<<1); break;
    case snd_32_linear: return(size>>1); break;
    case snd_32_float: return(size>>1); break;
    case snd_24_linear: return((size>>2)+(size>>1)); break;
    case snd_64_double: return(size>>2); break;
    }
}

int be_or_le(int n1, int n2)
{
  /* check both big and little endian versions for equality */
  return ((n1 == n2) ||
	  (((n1>>24)&0xff)+((n1<<24)&0xff000000)+((n1>>8)&0xff00)+((n1<<8)&0xff0000)) == n2);
}


static void read_bicsf_header (int chan);


/* ------------------------------------ NeXT (or Sun) -------------------------------- 
 * formats agree at least on the set given below between NeXT and Sun 
 * according to the Sox program, Dec format is the same.
 * SGI also supports this format without change, and AFsp is an extension of it. 
 * This is, in my opinion, the best existing sound-file header format. 
 * 
 *   0:  ".snd"
 *   4:  data_location (bytes) (not necessarily word aligned on Sun)
 *   8:  data_size (bytes)
 *   12: data format indicator -- see table below
 *   16: srate (int)
 *   20: chans
 *   24: comment start
 *   
 * in an AFsp file, the first 4 bytes of the comment are "AFsp",
 * see headers.lisp for readers/writers of AFsp fields
 * for bicsf, the integer at 28 is 107364 or 107415
 *
 * on NeXTStep, always big-endian.
 */

void read_next_header (int chan)
{
#ifdef MAC
  #pragma unused(chan)
#endif
  int maybe_bicsf;
  type_specifier = (*((int *)(hdrbuf)));
  data_location = clm_int((*((int *)(hdrbuf+4))));
  data_size = clm_int((*((int *)(hdrbuf+8))));
  original_sound_format = clm_int((*((int *)(hdrbuf+12))));
  switch (original_sound_format) 
    { /* defined in /usr/include/sound/soundstruct.h */
      /* see headers.lisp for a table of all known format names */
    case 1: sound_format = snd_8_mulaw; break;
    case 2: sound_format = snd_8_linear; break;
    case 3: sound_format = snd_16_linear; break;
    case 4: sound_format = snd_24_linear; break;
    case 5: sound_format = snd_32_linear; break;
    case 6: sound_format = snd_32_float; break;
    case 7: sound_format = snd_64_double; break;
    case 18: sound_format = snd_16_linear; break; 
      /* "emphasized"
       * I believe the de-emphasis filter is:
       *         y(n) = 0.997646 x(n) + 1.99516 x(n-1) + 0.997512 x(n-2) - 1.99516 y(n-1) - 0.995159 y(n-2)
       * from an active filter design by Atau Tanaka, Perry Cook, and Nick Porcaro, kindly supplied to me by JOS.
       *
       * Perhaps just as good is Xavier Serra's de-emphasis filter: y(n) = x(n) + .9 y(n-1)
       *
       * for now, I'll just pass it through unfiltered.
       */
    case 27: sound_format = snd_8_alaw; break;
    default: sound_format = snd_unsupported; break;
    }
  srate = clm_int((*((int *)(hdrbuf+16))));
  chans = clm_int((*((int *)(hdrbuf+20))));
  comment_start = 24;
  comment_end = data_location - 1;
  header_distributed = 0;
  maybe_bicsf = clm_int((*((int *)(hdrbuf+28))));
  if (maybe_bicsf == 107364) read_bicsf_header(chan);
  data_size = c_snd_samples(sound_format,data_size);
}

void write_next_header (int chan, int srate, int chans, int loc, int siz, int format, char *comment, int len)
{
  int i,j;
  (*((int *)hdrbuf)) = (*(int *)I_DSND); /* ".snd" */
  (*((int *)(hdrbuf+4))) = clm_int(loc);
  (*((int *)(hdrbuf+8))) = clm_int(siz);
  if (format == snd_16_linear)
    (*((int *)(hdrbuf+12))) = clm_int(3);
  else 
    (*((int *)(hdrbuf+12))) = clm_int(1);
  (*((int *)(hdrbuf+16))) = clm_int(srate);
  (*((int *)(hdrbuf+20))) = clm_int(chans);
  write(chan,hdrbuf,24);
  j = 0;
  for (i=0;i<len;i++) 
    {
      hdrbuf[j]=comment[i];
      j++;
      if (j == HDRBUFSIZ) 
	{
	  write(chan,hdrbuf,HDRBUFSIZ); /* not clm_write because this is an ASCII string of bytes */
	  j = 0;
	}
    }
  for (i=0;i<(loc-(len+24));i++) /* now fill left over bytes with nulls */
    {
      hdrbuf[j]=0;
      j++;
      if (j == HDRBUFSIZ) 
	{
	  write(chan,hdrbuf,HDRBUFSIZ);
	  j = 0;
	}
    }
  if (j != 0) write(chan,hdrbuf,j);
}

void update_next_header (int chan, int siz)
{
  lseek(chan,8L,0);
  (*((int *)(hdrbuf))) = clm_int(siz);
  write(chan,hdrbuf,4);
}



/* ------------------------------------ AIFF ------------------------------------ 
 *
 *  0: "FORM"
 *  4: size (bytes)
 *  8: "AIFF" or "AIFC" -- the latter includes compressed formats
 *
 *  Thereafter the file is organized into "chunks", each chunk being 
 *  a 4-byte identifer followed by an int (4-bytes) giving the chunk size
 *  not including the 8-byte header.  AIFF data is signed.
 *
 *  The chunks we want are "COMM", "SSND", and "APPL". See comments below for details.
 */

/* ieee-80 conversions -- design by committee! */
/* this code taken from CSound sources -- apparently originally written by Malcolm Slaney at Apple */

#define ULPOW2TO31	((unsigned long)0x80000000L)
#define DPOW2TO31	((double)2147483648.0)	/* 2^31 */

static double myUlongToDouble(unsigned long ul)
{
  double val;
  if(ul & ULPOW2TO31) val = DPOW2TO31 + (ul & (~ULPOW2TO31));
  else val = ul;
  return val;
}

static unsigned long myDoubleToUlong(double val)
{
  unsigned long ul;
  if(val < DPOW2TO31) ul = (unsigned long)val;
  else ul = ULPOW2TO31 | (unsigned long)(val-DPOW2TO31);
  return ul;
}

double ieee_80_to_double(char *p)
{
  char sign;
  short exp = 0;
  unsigned long mant1 = 0;
  unsigned long mant0 = 0;
  double val;
  exp = *p++;  exp <<= 8;  exp |= *p++;  sign = (exp & 0x8000) ? 1 : 0;  exp &= 0x7FFF;
  mant1 = *p++;  mant1 <<= 8;  mant1 |= *p++;  mant1 <<= 8;  mant1 |= *p++;  mant1 <<= 8;  mant1 |= *p++;
  mant0 = *p++;  mant0 <<= 8;  mant0 |= *p++;  mant0 <<= 8;  mant0 |= *p++;  mant0 <<= 8;  mant0 |= *p++;
  if(mant1 == 0 && mant0 == 0 && exp == 0 && sign == 0)
    return 0.0;
  else
    {
      val = myUlongToDouble(mant0) * pow(2.0,-63.0);
      val += myUlongToDouble(mant1) * pow(2.0,-31.0);
      val *= pow(2.0,((double) exp) - 16383.0);
      return sign ? -val : val;
    }
}

void double_to_ieee_80(double val, char *p)
{
  char sign = 0;
  short exp = 0;
  unsigned long mant1 = 0;
  unsigned long mant0 = 0;
  if(val < 0.0)	{  sign = 1;  val = -val; }
  if(val != 0.0)	/* val identically zero -> all elements zero */
    {
      exp = (short)(log(val)/log(2.0) + 16383.0);
      val *= pow(2.0, 31.0+16383.0-(double)exp);
      mant1 = myDoubleToUlong(val);
      val -= myUlongToDouble(mant1);
      val *= pow(2.0, 32.0);
      mant0 = myDoubleToUlong(val);
    }
  *p++ = ((sign<<7)|(exp>>8));  *p++ = 0xFF & exp;  
  *p++ = 0xFF & (mant1>>24);  *p++ = 0xFF & (mant1>>16);  *p++ = 0xFF & (mant1>> 8);  *p++ = 0xFF & (mant1);
  *p++ = 0xFF & (mant0>>24);  *p++ = 0xFF & (mant0>>16);  *p++ = 0xFF & (mant0>> 8);  *p++ = 0xFF & (mant0);
}

int ieee_80_to_long(char *hdrbuf, int loc)
{
  int i;
  i=ieee_80_to_double((char *)(hdrbuf+loc));
  return(i);
}

void long_to_ieee_80(int srate, char *hdrbuf, int loc)
{
  double fx;
  fx = srate;
  double_to_ieee_80(fx,(char *)(hdrbuf+loc));
}


int reset_hdrbuf(int chan, int loc)
{
  lseek(chan,loc,0);
  read(chan,hdrbuf,HDRBUFSIZ);
  return(loc+HDRBUFSIZ);
}

static int update_form_size, update_frames_size, update_frames_location, update_ssnd_location;

void read_aiff_header (int chan)
{
  /* we know we have checked for FORM xxxx AIFF|AIFC when we arrive here */
  /* as far as I can tell, the COMM block has the header data we seek, and the SSND block has the sound data */
  /* everything else will be ignored -- apparently we can depend on seeing a "chunk" name, then size */
  int chunksize,chunkname,offset,frames,curend,chunkloc,appl_name,happy;
  type_specifier = (*((int *)(hdrbuf)));
  chunkloc = 12;
  curend = 32;
  offset = 0;
  comment_start = 0;
  comment_end = 0;
  sound_format = snd_16_linear;
  header_distributed = 1;
  srate = 0;
  chans = 0;
  happy = 1;
  update_form_size = (*(int *)(hdrbuf+4));
  while (happy)
    {
      if ((chunkloc+8) > curend)  /* get enough data to read chunk header */
	{
	  offset += chunkloc;
	  curend = reset_hdrbuf(chan,chunkloc);
	  chunkloc = 0;
	}
      chunkname = (*(int *)(hdrbuf+chunkloc));
      chunksize = (*(int *)(hdrbuf+chunkloc+4));
      if (chunkname == (*(int *)I_COMM))
	{
	  if ((chunkloc+26) > curend) 
	    {
	      offset += chunkloc;
	      curend = reset_hdrbuf(chan,chunkloc);
	      chunkloc = 0;
	    }
	  chans = (*(short *)(hdrbuf+chunkloc+8));
	  frames = (*(int *)(hdrbuf+chunkloc+10));
	  update_frames_location = chunkloc+10+offset;
	  update_frames_size = frames;
	  if ((*(short *)(hdrbuf+chunkloc+14)) != 16)
	    { 
	      original_sound_format = ((*(short *)(hdrbuf+chunkloc+14)));
	      if (original_sound_format == 8) sound_format = snd_8_linear;
	      else sound_format = snd_unsupported;
	    }
	  data_size = (frames*2*chans);
	  srate = ieee_80_to_long(hdrbuf,chunkloc+16);
	}
      else
	{
	  if (chunkname == (*(int *)I_SSND))
	    {
	      if ((chunkloc+12) > curend) 
		{
		  offset += chunkloc;
		  curend = reset_hdrbuf(chan,chunkloc);
		  chunkloc = 0;
		}
	      update_ssnd_location = offset+chunkloc+4;
	      data_location = (*(int *)(hdrbuf+chunkloc+8)) + offset + 16 + chunkloc; /* Baroque! */
	      /* offset is where the hdrbuf is positioned in the file, chunkloc is the start */
	      /* of the chunk within hdrbuf, the sound data offset itself is at loc+8 and the */
	      /* 0-based location of the sound data is at the end of the chunk = 16 (8=header+4=offset+4=blocksize) */
	      happy = 0;
	    }
	  else
	    {
	      if (chunkname == (*(int *)I_ANNO))
		{
		  if ((chunkloc+8) > curend) 
		    {
		      offset += chunkloc;
		      curend = reset_hdrbuf(chan,chunkloc);
		      chunkloc = 0;
		    }
		  comment_start = chunkloc+8;
		  comment_end = chunkloc+7+chunksize;
		}
	      else
		/* ignored types include 'MARK' for a marker, 'INST' for looping control, 'MIDI' obvious */
		/* 'COMT' for a comment -- not used by CLM because it makes various dumb assumptions --  */
		/* for example, the length of the comment is limited to 65536 chars; then there are      */
		/* the "let's get really silly" chunks like 'NAME', 'AUTH', '(c) ',                      */
		/* 'AESD' for audio recording info, and finally, the one we're looking for here...       */
		if (chunkname == (*(int *)I_APPL))
		  {
		    if ((chunkloc+12) > curend) 
		      {
			offset += chunkloc;
			curend = reset_hdrbuf(chan,chunkloc);
			chunkloc = 0;
		      }
		    appl_name = (*(int *)(hdrbuf+chunkloc+8));
		    if (appl_name == (*(int *)I_CLM_))
		      {
			/* my own chunk has the arbitrary length comment I use (actually the ASCII    */
			/* representation of a lisp program evaluated in the CLU package) to handle mix et al. */
			/* It is nothing more than the actual string -- remember to pad to even length here. */
			comment_start = offset + chunkloc + 12;
			comment_end = comment_start + chunksize - 5;
		      }
		  }
	    }
	}
      chunkloc += (8+chunksize);
    }
  data_size = c_snd_samples(sound_format,data_size);
}

void write_aiff_header (int chan, int srate, int chans, int loc, int siz, int format, char *comment, int len)
{
  /* we write the simplest possible AIFF header: AIFF | COMM | APPL-CLM_ if needed | SSND eof. */
  /* the assumption being that we're going to be appending sound data once the header is out   */
  int offset,i,j,lenhdr,extra,curend;
  #pragma unused(loc,format)
  lenhdr=0;
  if (len != 0) lenhdr = 12;
  (*(int *)hdrbuf) = (*(int *)I_FORM);
  (*(int *)(hdrbuf+4)) = len+38+16+siz+lenhdr;
  (*(int *)(hdrbuf+8)) = (*(int *)I_AIFF);
  (*(int *)(hdrbuf+12)) = (*(int *)I_COMM);
  (*(int *)(hdrbuf+16)) = 18;
  (*(short *)(hdrbuf+20)) = (short)chans;
  (*(int *)(hdrbuf+22)) = (int)(siz / (chans*2));
  (*(short *)(hdrbuf+26)) = 16;
  long_to_ieee_80(srate,hdrbuf,28);
  offset = 38;
  i = 38;
  extra = 0;
  curend = 0;
  if (len != 0)
    {
      offset += len+12;
      if ((len % 4) != 0)
	extra = (4 - (len % 4));
      (*(int *)(hdrbuf+38)) = (*(int *)I_APPL);
      (*(int *)(hdrbuf+42)) = len+4+extra;
      (*(int *)(hdrbuf+46)) = (*(int *)I_CLM_);
      i = 50;
      for (j=0;j<len;j++)
	{
	  if (i == HDRBUFSIZ)
	    {
	      curend += HDRBUFSIZ;
	      write(chan,hdrbuf,HDRBUFSIZ);
	      i=0;
	    }
	  hdrbuf[i]=comment[j];
	  i++;
	}
      if (extra != 0)
	{
	  if ((i+extra) > HDRBUFSIZ)
	    {
	      curend += i;
	      write(chan,hdrbuf,i);
	      i=0;
	    }
	  for (j=0;j<extra;j++)
	    {
	      hdrbuf[i] = 0;
	      i++;
	    }
	}
    }
  if ((i+16) > HDRBUFSIZ)
    {
      curend += i;
      write(chan,hdrbuf,i);
      i = 0;
    }
  (*(int *)(hdrbuf+i)) = (*(int *)I_SSND);
  (*(int *)(hdrbuf+i+4)) = (siz+8);
  (*(int *)(hdrbuf+i+8)) = 0;  /* "offset" */
  (*(int *)(hdrbuf+i+12)) = 0;  /* "blocksize " */
  data_location = i+16+curend;
  write(chan,hdrbuf,i+16);
}


void update_aiff_header (int chan, int siz)
{
  /* we apparently have to make sure the form size and the data size are correct */
  /* assumed here that we'll only be updating our own AIFF files */
  /* There are 3 such locations -- the 2nd word of the file which is the overall form size, */
  /* the frames variable in the COMM chunk, and the chunk-size variable in the SSND chunk */
  read(chan,hdrbuf,32);
  read_aiff_header(chan);
  lseek(chan,4L,0);
  (*(int *)hdrbuf) = (siz+update_form_size);
  write(chan,hdrbuf,4);
  lseek(chan,update_frames_location,0);
  (*(int *)hdrbuf) = ((siz / (chans*2))+update_frames_size);
  write(chan,hdrbuf,4);
  lseek(chan,update_ssnd_location,0);
  (*(int *)hdrbuf) = siz+8;
  write(chan,hdrbuf,4);
}




/* ------------------------------------ RIFF ------------------------------------
 * nearly identical to AIFF from our point of view
 *
 *   0: "RIFF"
 *   4: size
 *   8: "WAVE"
 *  
 *   rest very similar to AIFF -- on the Mac, these files contain big-endian data but little-endian headers!
 */

#ifdef __LITTLE_ENDIAN__
#define le(n)  n
#define les(n) n
#else
int le(int n) {return(((n>>24)&0xff) + ((n<<24)&0xff000000) + ((n>>8)&0xff00) + ((n<<8)&0xff0000));}
short les(short n) {return( ((n>>8)&0xff) + ((n<<8)&0xff00));}
#endif

void read_riff_header (int chan)
{
  /* we know we have checked for RIFF xxxx WAVE when we arrive here */
  int chunksize,chunkname,offset,curend,chunkloc,happy,bits;
  type_specifier = (*((int *)(hdrbuf)));
  chunkloc = 12;
  curend = 32;
  offset = 0;
  comment_start = 0;
  comment_end = 0;
  header_distributed = 1;
  sound_format = snd_unsupported;
  srate = 0;
  chans = 0;
  happy = 1;
  update_form_size = le((*(int *)(hdrbuf+4)));
  while (happy)
    {
      if ((chunkloc+8) > curend)  /* get enough data to read chunk header */
	{
	  offset += chunkloc;
	  curend = reset_hdrbuf(chan,chunkloc);
	  chunkloc = 0;
	}
      chunkname = (*(int *)(hdrbuf+chunkloc)); /* was le */
      chunksize = le((*(int *)(hdrbuf+chunkloc+4)));
      if (chunkname == (*(int *)I_fmt_))
	{
	  if ((chunkloc+26) > curend) 
	    {
	      offset += chunkloc;
	      curend = reset_hdrbuf(chan,chunkloc);
	      chunkloc = 0;
	    }
	  /*
	   * 8:  short format code        --1=PCM for example
	   * 10: short chans              --1
	   * 12: long rate                --48000 (0xbb80)
	   * 16: long ave rate            --65655 (0x10077)
	   * 20: short align              --2
	   * 22: short data size (bits)   --16
	   *
	   *  R I  F F  # #  # #  W A  V E  f m  t sp
	   *  5249 4646 f851 0500 5741 5645 666d 7420
	   *  e40f 0000 0100 0100 80bb 0000 0077 0100
	   *  0200 1000 0000 0000 0000 0000 0000 0000
	   *  
	   *  #x000551f8 = 348664 = size in bytes - 8
	   *  #x00000fe4 = 4068 [fmt_ chunk size?]
	   *
	   * formats are 6=alaw, 7=mulaw, 0x15=digistd, 0x16=digifix, (what are these really?)
	   *             0x102=ibm alaw and 0x101=ibm mulaw, (what's the difference?)
	   *             1=PCM, 2=ADPCM (not supported in CLM)
	   * see headers.lisp for a table of all known format names 
	   */
	  original_sound_format = les(((*(short *)(hdrbuf+chunkloc+8))));
	  chans = les((*(short *)(hdrbuf+chunkloc+10)));
	  srate = le((*(int *)(hdrbuf+chunkloc+12)));
	  bits = les((*(short *)(hdrbuf+22)));
	  if (bits == 16) sound_format = snd_16_linear; else
	    if ((original_sound_format == 6) && (bits == 8)) sound_format = snd_8_alaw; else
	      if ((original_sound_format == 7) && (bits == 8)) sound_format = snd_8_mulaw; else
		if (original_sound_format == 1)
		    {
		      if (bits == 8) sound_format = snd_8_linear; else
			if (bits == 32) sound_format = snd_32_linear;
		    }
		else
		  if ((original_sound_format == 0x102) && (bits == 8)) sound_format = snd_8_alaw; else
		    if ((original_sound_format == 0x101) && (bits == 8)) sound_format = snd_8_mulaw;
	}
      else
	{
	  if (chunkname == (*(int *)I_data))
	    {
	      if ((chunkloc+12) > curend) 
		{
		  offset += chunkloc;
		  curend = reset_hdrbuf(chan,chunkloc);
		  chunkloc = 0;
		}
	      update_ssnd_location = offset+chunkloc+4;
	      data_location = offset + 8 + chunkloc;
	      data_size = le((*(int *)(hdrbuf+chunkloc+4)));
	      happy = 0;
	    }
	  else
	    if (chunkname == (*(int *)I_clm_))
	      {
		if ((chunkloc+8) > curend) 
		  {
		    offset += chunkloc;
		    curend = reset_hdrbuf(chan,chunkloc);
		    chunkloc = 0;
		  }
		comment_start = offset + chunkloc + 8;
		comment_end = comment_start + chunksize - 1; /* end of comment not start of next chunk */
	      }
	}
     chunkloc += (8+chunksize);
    }
  data_size = c_snd_samples(sound_format,data_size);
}

void write_riff_header (int chan, int srate, int chans, int loc, int siz, int format, char *comment, int len)
{
  int offset,i,j,lenhdr,extra,curend;
#pragma unused(loc,format)
  lenhdr=0;
  if (len != 0) lenhdr = 12;
  (*(int *)hdrbuf) = (*(int *)I_RIFF);
  (*(int *)(hdrbuf+4)) = le(len+36+14+siz+lenhdr-8);
  (*(int *)(hdrbuf+8)) = (*(int *)I_WAVE);
  (*(int *)(hdrbuf+12)) = (*(int *)I_fmt_);
  (*(int *)(hdrbuf+16)) = le(26-8);
  (*(short *)(hdrbuf+20)) = les(1);
  (*(short *)(hdrbuf+22)) = les((short)chans);
  (*(int *)(hdrbuf+24)) = le(srate);
  (*(short *)(hdrbuf+28)) = les(2);
  (*(int *)(hdrbuf+30)) = le(16);
  offset = 34;
  i = 34;
  extra = 0;
  curend = 0;
  if (len != 0)
    {
      offset += len+12;
      if ((len % 4) != 0)
	extra = (4 - (len % 4));
      (*(int *)(hdrbuf+38)) = (*(int *)I_clm_);
      (*(int *)(hdrbuf+42)) = len+extra;
      i = 42;
      for (j=0;j<len;j++)
	{
	  if (i == HDRBUFSIZ)
	    {
	      curend += HDRBUFSIZ;
	      write(chan,hdrbuf,HDRBUFSIZ);
	      i=0;
	    }
	  hdrbuf[i]=comment[j];
	  i++;
	}
      if (extra != 0)
	{
	  if ((i+extra) > HDRBUFSIZ)
	    {
	      curend += i;
	      write(chan,hdrbuf,i);
	      i=0;
	    }
	  for (j=0;j<extra;j++)
	    {
	      hdrbuf[i] = 0;
	      i++;
	    }
	}
    }
  if ((i+16) > HDRBUFSIZ)
    {
      curend += i;
      write(chan,hdrbuf,i);
      i = 0;
    }
  (*(int *)(hdrbuf+i)) = (*(int *)I_data);
  (*(int *)(hdrbuf+i+4)) = le(siz);
  data_location = i+8+curend;
  write(chan,hdrbuf,i+8);
}


void update_riff_header (int chan, int siz)
{
  read(chan,hdrbuf,32);
  read_riff_header(chan);
  lseek(chan,4L,0);
  (*(int *)hdrbuf) = le((siz+update_form_size));
  write(chan,hdrbuf,4);
  lseek(chan,update_ssnd_location,0);
  (*(int *)hdrbuf) = le(siz+8);
  write(chan,hdrbuf,4);
}



/* ------------------------------------ NIST ------------------------------------ 
 * read-only in CLM 
 * 
 *   0: "NIST_1A"
 *   8: data_location as ASCII representation of integer (apparently always "   1024")
 *  16: start of complicated header -- see below for details
 *
 *  The most recent version of the SPHERE package is available
 *  via anonymous ftp from jaguar.ncsl.nist.gov [129.6.48.157] in
 *  compressed tar form as "sphere-v.tar.Z" (where "v" is the version
 *  code).
 *
 *  here's an example:
 *
 *  NIST_1A
 *     1024
 *  database_id -s5 TIMIT
 *  database_version -s3 1.0
 *  utterance_id -s8 aks0_sa1
 *  channel_count -i 1
 *  sample_count -i 63488
 *  sample_rate -i 16000
 *  sample_min -i -6967
 *  sample_max -i 7710
 *  sample_n_bytes -i 2
 *  sample_byte_format -s2 01
 *  sample_sig_bits -i 16
 *  end_head
 */

int decode_nist_value (char *str,int base,int end)
{
  /* can be -i -r or -snnn where nnn=ascii rep of integer = len of string (!) */
  /* we'll deal only with integer fields */
  int i,j;
  char value[80];
  i=base;
  while ((i<end) && (str[i] != '-')) i++; /* look for -i or whatever */
  while ((i<end) && (str[i] != ' ')) i++; /* look for space after it */
  i++;
  for (j=0;i<end;j++,i++)
    value[j]=str[i];
  value[j]=0;
  sscanf(value,"%d",&i);
  return(i);
}

void read_nist_header (int chan)
{
  char str[80],name[80];
  int happy = 1;
  int k,hend,curbase,j,n,nm,samples,bytes;
  type_specifier = (*((int *)(hdrbuf))); /* the actual id is "NIST_1A" */
  for (k=8;k<16;k++) str[k-8]=hdrbuf[k];
  sscanf(str,"%d",&data_location);       /* always "1024" */
  n = 16;
  hend = 32;
  k=0;
  curbase = 0;
  samples = 0;
  bytes = 0;
  srate = 0;
  chans = 0;
  comment_start = 16;
  comment_end = 16;
  for (j=0;j<80;j++) str[j]=' ';  
  while (happy) 
    {
      /* much as in xIFF files, march through the file looking for the data we're after */
      /* in this case we munch a character at a time... */
      str[k] = hdrbuf[n];
      if ((((str[k] == '\0') || (str[k] == '\n')) || ((curbase+n+1) >= data_location)) || (k == 79))
	{
	  /* got a complete record (assuming no embedded newlines, of course) */
	  /* now look for a record we care about and decode it */
	  nm = 0;
	  while (str[nm] != ' ')
	    {
	      name[nm] = str[nm];
	      nm++;
	    }
	  name[nm]=0;
	  if (strcmp(name,"sample_rate") == 0) srate = decode_nist_value(str,nm,k); else
	    if (strcmp(name,"channel_count") == 0) chans = decode_nist_value(str,nm,k); else
	      if (strcmp(name,"end_head") == 0) {happy = 0; comment_end=curbase+n-1;} else
		if (strcmp(name,"sample_count") == 0) samples = decode_nist_value(str,nm,k); else
		  if ((bytes == 0) && (strcmp(name,"sample_n_bytes") == 0)) bytes = decode_nist_value(str,nm,k); else
		    if ((bytes == 0) && (strcmp(name,"sample_sig_bits") == 0)) {bytes = decode_nist_value(str,nm,k); bytes = (bytes>>3);}
	  for (j=0;j<=k;j++) str[j]=' ';
	  k=0;
	  if ((curbase+n+1) > 512) happy=0;
	}
      else
	k++;
      n++;
      if (n >= hend)
	{
	  curbase += hend;
	  n = 0;
	  read(chan,hdrbuf,HDRBUFSIZ);
	  hend = HDRBUFSIZ;
	}
    }
  data_size = samples*bytes;
  if (bytes == 2) sound_format = snd_16_linear; else sound_format = snd_8_mulaw;
  data_size = c_snd_samples(sound_format,data_size);
  header_distributed = 1;
}



/* ------------------------------------ BICSF ------------------------------------ 
 * read-only in CLM  (actually, this is EBICSF and the old BICSF is called IRCAM below)
 *
 * 0-28: NeXT-compatible header, read by read_next_header above.
 *   28: bicsf magic number (107364 or trouble)
 *   32: srate as a 32-bit float
 *   36: chans
 *   40: data format indicator (2=16-bit linear, 4=32-bit float)
 *   44: begin chunks, if any
 *
 * followed by AIFF-style chunked header info with chunks like:
 *
 *   COMM size comment
 *   MAXA size {max amps (up to 4)} {frame offsets) time-tag unix msec counter
 *   CUE, PRNT, ENV etc 
 */

static void read_bicsf_header (int chan)
{
  type_specifier = (*((int *)(hdrbuf+28)));
  header_type = BICSF_sound_file;
  if (data_size == 0) data_size = (lseek(chan,0L,2)-data_location);
  original_sound_format = clm_int((*((int *)(hdrbuf+40))));
  switch (original_sound_format) 
    {
    case 2: sound_format = snd_16_linear; break;
    case 4: sound_format = snd_32_float; break;
    default: break;
    }

  /* now check for a COMM chunk, setting the comment pointers */
  {
    int chunksize,chunkname,offset,curend,chunkloc,happy;
    chunkloc = 28+16; /* next header + magic number, srate, chans, packing, then chunks, I think */
    curend = HDRBUFSIZ;
    offset = 0;
    comment_start = 0;
    comment_end = 0;
    header_distributed = 1;
    happy = 1;
    while (happy)
      {
        if (curend >= data_location) 
	  happy = 0;
	else
	  {
	    if ((chunkloc+8) > curend)  /* get enough data to read chunk header */
	      {
		offset += chunkloc;
		curend = reset_hdrbuf(chan,chunkloc);
		chunkloc = 0;
	      }
	    chunkname = (*(int *)(hdrbuf+chunkloc));
	    chunksize = (*(int *)(hdrbuf+chunkloc+4));
	    if (chunkname == (*(int *)I_COMM))
	      {
		comment_start = curend+chunkloc+8;
		comment_end = comment_start + chunksize;
		happy = 0;
	      }
	    else
	      {
		if ((chunkname == 0) || (chunksize == 0)) 
		  happy = 0;
	      }
	    chunkloc += (8+chunksize);
	  }
      }
  }
  /* from here we fall back into read_next_header */
}



/* ------------------------------------ IRCAM ------------------------------------ 
 * read-only in CLM (old-style BICSF)
 *
 *    0: 0x1a364
 *    4: srate as a 32-bit float
 *    8: chans
 *   12: data format indicator (2=16-bit linear, 4=32-bit float)
 *   16: comment start -- how to tell if it's a real comment?
 * 1024: data start
 */

static void read_ircam_header (int chan)
{
  type_specifier = (*((int *)(hdrbuf)));
  data_location = 1024;
  data_size = (lseek(chan,0L,2)-1024);
  original_sound_format = clm_int((*((int *)(hdrbuf+12))));
  switch (original_sound_format) 
    {
    case 2: sound_format = snd_16_linear; break;
    case 4: sound_format = snd_32_float; break;
    default: sound_format = snd_unsupported; break;
    }
  srate = (*((float *)(hdrbuf+4)));
  chans = clm_int((*((int *)(hdrbuf+8))));
  comment_start = 16;
  comment_end = 1023;
  header_distributed = 0;
  data_size = c_snd_samples(sound_format,data_size);
}



/* ------------------------------------ AVR -------------------------------------- 
 * read-only in CLM 
 * 
 *   0: "2BIT"
 *   4: sample name (null padded ASCII)
 *  12: chans (short) (0=mono, -1=stereo)
 *  14: sample size (8 or 16 bit) (short) (value is 8 or 16)
 *  16: sample format (signed or unsigned) (short) (0=unsigned, -1=signed)
 *  18: loop, 20: midi
 *  22: srate (integer)
 *  26: length in samples
 *  30: loop beg, 34: loop end, 38: midi, 40: compression, 42: nada, 44: name
 *  64: comment (limited to 64 bytes)
 * 128: data start
 */

void read_avr_header(int chan)
{
#ifdef MAC
  #pragma unused(chan)
#endif
  int dsize,dsigned,i;
  chans = (*((short *)(hdrbuf+12)));
  if (chans == 0) chans=1; else chans=2;
  data_location = 128;
  data_size = (*((int *)(hdrbuf+26)));
  comment_start = 64;
  i = 64;
  if (HDRBUFSIZ >= 128)
    {
      while ((i < 128) && (hdrbuf[i] != 0)) i++;
      comment_end = (i-1);
    }
  else comment_end = 127;
  header_distributed = 0;
  srate = (*((int *)(hdrbuf+22)));
  dsize = (*((short *)(hdrbuf+14)));
  dsigned = (*((short *)(hdrbuf+16)));
  if (dsize == 16) 
    {
      if (dsigned == 0)
	sound_format = snd_unsupported;  /* unsigned 16-bit!? */
      else sound_format = snd_16_linear;
    }
  else 
    {
      if (dsigned == 0) 
	sound_format = snd_8_unsigned;
      else sound_format = snd_8_linear;
    }
}




/* ------------------------------------ VOC -------------------------------------- 
 * read-only in CLM 
 *
 *   0: "Creative Voice File" followed by a couple ctrl-Z ('32) (swapped data)
 *  20: header end (short)
 * [20]: first block:
 *     block code, 1=data, 0=end
 *     block len as 24 bit int(?)
 *     if data, then rate code (byte), then data
 */

void read_voc_header(int chan)
{
  int type,happy,len,curbase,file_len;
  sound_format = snd_8_unsigned;
  chans = 1;
  happy = 1;
  comment_start = 0;
  comment_end = 0;
  file_len=lseek(chan,0L,2);
  curbase = ((int)(hdrbuf[20])+(((int)(hdrbuf[21]))<<8));
  lseek(chan,curbase,0);
  read(chan,hdrbuf,HDRBUFSIZ);
  while (happy)
    {
      type = (int)(hdrbuf[0]);
      len=(((int)hdrbuf[3])<<16)+(((int)hdrbuf[2])<<8)+(((int)hdrbuf[1]));
      if (type == 1)
	{
	  data_size = len-3;
	  data_location = curbase+6;
	  srate = (hdrbuf[4]&0xff);
	  if (hdrbuf[5] != 0) {printf("uh oh:%d ",hdrbuf[5]); fflush(stdout);}
	  happy = 0;
	}
      else
	{
	  if ((len+curbase)<file_len)
	    {
	      lseek(chan,curbase+len+4,0);
	      read(chan,hdrbuf,HDRBUFSIZ);
	      curbase += len;
	    }
	  else happy = 0;
	}
    }
  srate = (int)(1000000.0/(256-srate));
  data_size = c_snd_samples(sound_format,data_size);
}



/* ------------------------------------ SNDT -------------------------------------
 * read-only in CLM 
 *
 * this taken from sndrtool.c (sox-10):
 *   0: "SOUND"
 *   6: 0x1a
 *   8-11: 0
 *  12-15: nsamples
 *  16-19: 0
 *  20-23: nsamples
 *  24-25: srate
 *  26-27: 0
 *  28-29: 10
 *  30-31: 4
 *  32-> : <filename> "- File created by Sound Exchange"
 *  .->95: 0
 */

void read_sndt_header(int chan)
{
  #pragma unused(chan);
  sound_format = snd_8_unsigned;
  chans = 1;
  comment_start = 0;
  comment_end = 0;
  srate = (*(short *)(hdrbuf+24));
  data_location = 96;
  data_size = (*(int *)(hdrbuf+12));
  if (data_size != (*(int *)(hdrbuf+20))) {printf("uh oh"); fflush(stdout);}
  header_distributed = 0;
  data_size = c_snd_samples(sound_format,data_size);
}



/* ------------------------------------ SMP ------------------------------------- 
 * read-only in CLM 
 *
 *  0: "SOUND SAMPLE DATA "
 * 18: "2.1 "
 * 22-81: comment
 * 82-111: sample name
 * header 112 bytes
 * long samples (bytes=samples*2)
 * then data start
 * data
 * trailer:
 * short reserved, 88 bytes of "loop" data, 8*14 bytes of markers
 * byte for MIDI -- all this junk even for straight data
 * long rate
 *
 * always little endian
 */

void read_smp_header(int chan)
{
  sound_format = snd_16_linear;
  chans = 1;
  comment_start=22;
  comment_end=81;
  data_location = 116;
  lseek(chan,112,0);
  read(chan,hdrbuf,4);
  data_size = (((int)hdrbuf[3])<<24)+(((int)hdrbuf[2])<<16)+(((int)hdrbuf[1])<<8)+(((int)(hdrbuf[0])));
  lseek(chan,data_size+116+88+8*14+1,0);
  read(chans,hdrbuf,4);
  srate = (((int)hdrbuf[3])<<24)+(((int)hdrbuf[2])<<16)+(((int)hdrbuf[1])<<8)+(((int)(hdrbuf[0])));
  data_size = c_snd_samples(sound_format,data_size);
}




/* ------------------------------------ ESPS ------------------------------------- 
 * read-only in CLM 
 *
 *   16: 0x00006a1a or 0x1a6a0000
 *  136: if not 0, chans + format = 32-bit float
 *  144: if not 0, chans + format = 16-bit linear
 *  see below for srate search
 * 
 *   from AFgetInfoES.c:
 * 
 *       Bytes     Type    Contents
 *      8 -> 11    --     Header size (bytes)
 *     12 -> 15    int    Sampled data record size
 *     16 -> 19    int    File identifier
 *     40 -> 65    char   File creation date
 *    124 -> 127   int    Number of samples (may indicate zero) -- is that an error?
 *    132 -> 135   int    Number of doubles in a data record
 *    136 -> 139   int    Number of floats in a data record
 *    140 -> 143   int    Number of longs in a data record
 *    144 -> 147   int    Number of shorts in a data record
 *    148 -> 151   int    Number of chars in a data record
 *    160 -> 167   char   User name 
 *    333 -> H-1   --     "Generic" header items, including "record_freq" {followed by a "double8"=64-bit float}
 *      H -> ...   --     Audio data
 */

int decode_esps_double (char *buf, int base)
{
  /* "8 byte double" */
  double gad;
  char fl[8];
  int i;
#ifdef __LITTLE_ENDIAN__
  for (i=0;i<8;i++) fl[i]=buf[i+base];
#else
  for (i=0;i<8;i++) fl[7-i]=buf[i+base];
#endif
  gad = (*((double *)(fl)));
  i = (int)gad;
  return(i);
}

void read_esps_header (int chan)
{
  int siz;
  char str[80];
  int happy = 1;
  int k,hend,curbase,j,n;
  data_location = clm_int((*(int *)(hdrbuf+8)));
  siz = lseek(chan,0L,2);
  lseek(chan,136,0);
  data_size = siz - data_location;
  header_distributed = 0;
  srate = 0;
  comment_start = 0;
  comment_end = 0;
  read(chan,hdrbuf,HDRBUFSIZ);
  chans = clm_int((*(int *)(hdrbuf+8))); /* 144 */
  if (chans != 0) 
    sound_format = snd_16_linear; 
  else
    {
      chans = clm_int((*(int *)(hdrbuf)));  /* 136 */
      if (chans != 0) sound_format = snd_32_float;
    }
  /* search for "record_freq" to get srate */
  lseek(chan,333,0);
  read(chan,hdrbuf,HDRBUFSIZ);
  curbase=333;
  hend=curbase+HDRBUFSIZ;
  k=0;
  n=0;
  for (j=0;j<80;j++) str[j]=' ';  
  while (happy) 
    {
      str[k] = hdrbuf[n];
      if ((str[k] == 'q') || (str[k] == 3) || ((curbase+n+1) >= data_location) || (k == 78))
	{
	  str[k+1]=0;
	  if (strcmp(str,"record_freq") == 0) 
	    {
	      if ((n+11)<HDRBUFSIZ)
		curbase=n+3; /* lessee, 1 ("q") + 1 (null) + 1 to grow on? */
	      else
		{
		  lseek(chan,curbase+n+3,0);
		  read(chan,hdrbuf,8);
		  curbase=0;
		}
	      srate = decode_esps_double(hdrbuf,curbase);
	      happy = 0;
	    }
	  if ((curbase+n+1) >= data_location) happy = 0;
	  k=0;
	}
      else
	k++;
      n++;
      if (n >= hend)
	{
	  curbase += hend;
	  n = 0;
	  read(chan,hdrbuf,HDRBUFSIZ);
	  hend = HDRBUFSIZ;
	}
    }
  data_size = c_snd_samples(sound_format,data_size);
}

 
 
/* ------------------------------------ INRS ------------------------------------- 
 * read-only in CLM 
 * 
 *   from AFgetInfoIN.c:
 * 
 *    INRS-Telecommunications audio file:
 *       Bytes     Type    Contents
 *      0 ->  3    float  Sampling Frequency (VAX float format)
 *      6 -> 25    char   Creation time (e.g. Jun 12 16:52:50 1990)
 *     26 -> 29    int    Number of speech samples in the file
 *   The data in an INRS-Telecommunications audio file is in 16-bit integer
 *   format. Header is always 512 bytes.  Always mono.
 * 
 */

void read_inrs_header (int chan)
{
  int siz;
  siz = lseek(chan,0L,2);
  comment_start = 0;
  comment_end = 0;
  header_distributed = 0;
  sound_format = INRS_sound_file;
  srate = (*((int *)hdrbuf));
  chans = 1;
  data_location = 512;
  data_size = siz-512;
}




/* ------------------------------------ 8SVX ------------------------------------- 
 * read-only in CLM 
 *
 * very similar to AIFF:
 *  "BODY" => [4] samples [n] data
 *  "VHDR" => srate (short)
 *  "CHAN" => chans
 *  "ANNO" and "NAME"
 */

void read_8svx_header (int chan)
{
  int chunksize,chunkname,offset,curend,chunkloc,happy;
  type_specifier = (*((int *)(hdrbuf)));
  chunkloc = 12;
  curend = 32;
  offset = 0;
  comment_start = 0;
  comment_end = 0;
  sound_format = snd_8_linear;
  header_distributed = 1;
  srate = 0;
  chans = 0;
  happy = 1;
  update_form_size = (*(int *)(hdrbuf+4));
  while (happy)
    {
      if ((chunkloc+8) > curend)  /* get enough data to read chunk header */
	{
	  offset += chunkloc;
	  curend = reset_hdrbuf(chan,chunkloc);
	  chunkloc = 0;
	}
      chunkname = (*(int *)(hdrbuf+chunkloc));
      chunksize = (*(int *)(hdrbuf+chunkloc+4));
      if (chunkname == (*(int *)I_CHAN))
	{
	  if ((chunkloc+12) > curend) 
	    {
	      offset += chunkloc;
	      curend = reset_hdrbuf(chan,chunkloc);
	      chunkloc = 0;
	    }
	  chans = (*(int *)(hdrbuf+chunkloc+8));
	  chans = (chans & 0x01) + ((chans & 0x02) >> 1) + ((chans & 0x04) >> 2) + ((chans & 0x08) >> 3);
	  /* what in heaven's name is this?  Each bit corresponds to a channel? */
	}
      else
	{
	  if (chunkname == (*(int *)I_VHDR))
	    {
	      if ((chunkloc+12) > curend) 
		{
		  offset += chunkloc;
		  curend = reset_hdrbuf(chan,chunkloc);
		  chunkloc = 0;
		}
	      srate = (*(short *)(hdrbuf+chunkloc+8));
	    }
	  else
	    {
	      if (chunkname == (*(int *)I_ANNO))
		{
		  if ((chunkloc+8) > curend) 
		    {
		      offset += chunkloc;
		      curend = reset_hdrbuf(chan,chunkloc);
		      chunkloc = 0;
		    }
		  comment_start = chunkloc+8;
		  comment_end = chunkloc+7+chunksize;
		}
	      else
		{
		  if (chunkname == (*(int *)I_BODY))
		    {
		      if ((chunkloc+12) > curend) 
			{
			  offset += chunkloc;
			  curend = reset_hdrbuf(chan,chunkloc);
			  chunkloc = 0;
			}
		      data_size = (*(int *)(hdrbuf+chunkloc+8));
		      data_location = chunkloc+12;
		    }
		}
	    }
	}
      chunkloc += (8+chunksize);
    }
  data_size = c_snd_samples(sound_format,data_size);
}



/* ------------------------------------ no header and Sound Designer II ------------------------------------- 
 *
 * Sound Designer II data fork is a raw data file -- interleaved channels, bytes in sequence.
 *   The associated resource fork under "STR " has "sample-size" "sample-rate" and "channels".
 *   Similarly the resources "sdDD" id 1000, "sdML" id 1000, and "sdLL" id 1000 return pascal records of
 *   respectively Document Data, Markers and text, Loop data.  I don't see any useful info in any of these
 *   except perhaps the file comment in the "sdDD" record of type str255 10 bytes in (I think).
 *   See headers.lisp for a reader for some of this stuff.
 *
 * read-only in CLM 
 */

void read_no_header (int chan)
{
  #pragma unused(chan);
  srate = 44100;
  chans = 2;
  comment_start = 0;
  comment_end = 0;
  header_distributed = 0;
  data_location = 0;
  data_size = lseek(chan,0L,2);
  /* read-header in headers.lisp will ask the user if this is really what he wants */
  /* The upshot is that fasmix (i.e. merge) cannot deal directly with raw sound data */
}





/* ------------------------------------ all together now ------------------------------------ */

void c_read_header_with_fd (int chan) /* internal optimization for merge.c */
{
  int word_0;
  read(chan,hdrbuf,32);
  header_type = unsupported_sound_file;
  word_0 = (*((int *)hdrbuf));
  if ((be_or_le(word_0,(*(int *)I_DSND))) || (be_or_le(word_0,I_DECN)))
    {
      header_type = NeXT_sound_file;
      read_next_header(chan);
    }
  else
    {
      if (be_or_le(word_0,(*(int *)I_FORM)))
	{
	  /* next 4 bytes are apparently the file size or something equally useless */
	  word_0 = (*((int *)(hdrbuf+8)));
	  if ((be_or_le(word_0,(*(int *)I_AIFF))) || (be_or_le(word_0,(*(int *)I_AIFC))))
	    { 
	      /* we'll assume the compression type is 'NONE' */
	      header_type = AIFF_sound_file;
	      read_aiff_header(chan);
	    }
	  else
	    {
	      if (be_or_le(word_0,(*(int *)I_8SVX)))
		{
		  header_type = SVX_sound_file;
		  read_8svx_header(chan);
		}
	    }
	}
      else
	{
	  if (be_or_le(word_0,(*(int *)I_RIFF)))
	    {
	      word_0 = (*((int *)(hdrbuf+8)));
	      if (be_or_le(word_0,(*(int *)I_WAVE)))
		{
		  header_type = RIFF_sound_file;
		  read_riff_header(chan);
		}
	    }
	  else
	    {
	      if (be_or_le(word_0,I_IRCAM))
		{
		  header_type = IRCAM_sound_file;
		  read_ircam_header(chan);
		}
	      else
		{
		  if (be_or_le(word_0,(*(int *)I_NIST)))
		    {
		      header_type = NIST_sound_file;
		      read_nist_header(chan);
		    }
		  else
		    {
		      if (be_or_le(word_0,(*(int *)I_SNDT)))
			{
			  if ((be_or_le(*(int *)I_SMP1,(*(int *)(hdrbuf+4)))) &&
			      (be_or_le(*(int *)I_SMP2,(*(int *)(hdrbuf+8)))))
			    {
			      header_type = SMP_sound_file;
			      read_smp_header(chan);
			    }
			  else
			    {
			      header_type = SNDT_sound_file;
			      read_sndt_header(chan);
			    }
			}
		      else
			{
			  if ((be_or_le(word_0,(*(int *)I_VOC0))) && 
			      (be_or_le(*(int *)(hdrbuf+4),(*(int *)I_VOC1))))
			    {
			      header_type = VOC_sound_file;
			      read_voc_header(chan);
			    }
			  else
			    {
			      if (be_or_le(word_0,(*(int *)I_AVR_)))
				{
				  header_type = AVR_sound_file;
				  read_avr_header(chan);
				}
			      else
				{ /* no recognized magic number at start -- poke around in possible header for other types */
				  /* ESPS is either 0x00006a1a or 0x1a6a0000 at byte 16 */
				  word_0 = (*(int *)(hdrbuf+16));
				  if (be_or_le(word_0,0x00006a1a))
				    {
				      header_type = ESPS_sound_file;
				      read_esps_header(chan);
				    }
				  else
				    {
				      int i,happy;
				      happy = 0;
				      for (i=0;i<NINRS;i++) 
					{
					  if (be_or_le(word_0,I_INRS[i]))
					    happy = 1;
					}
				      if (happy)
					{
					  header_type = INRS_sound_file;
					  read_inrs_header(chan);
					}
				      else
					{
					  header_type = raw_sound_file;
					  read_no_header(chan);
					}
				    }
				}
			    }
			}
		    }
		}
	    }
	}
    }
}

int c_read_header (char *name)
{
  int chan;
  chan = clm_open_read(name);
  if (chan == -1) return(-1);
  c_read_header_with_fd(chan);
  close(chan);  
  return(0);
}

int c_write_header (char *name, int type, int srate, int chans, int loc, int siz, int format, char *comment, int len)
{
  int chan;
  chan = clm_create(name);
  if (chan == -1) return(-1);
  switch (type)
    {
    case NeXT_sound_file: write_next_header(chan,srate,chans,loc,siz,format,comment,len); break;
    case AIFF_sound_file: write_aiff_header(chan,srate,chans,loc,siz,format,comment,len); break;
    case RIFF_sound_file: write_riff_header(chan,srate,chans,loc,siz,format,comment,len); break;
    default:
      {
	printf("cannot write this header!");
	fflush(stdout);
	break;
      }
    }
  close(chan);
  return(0);
}

void c_update_header_with_fd(int chan, int type, int siz)
{
  lseek(chan,0L,0);
  switch (type)
    {
    case NeXT_sound_file: update_next_header(chan,siz); break;
    case AIFF_sound_file: update_aiff_header(chan,siz); break;
    case RIFF_sound_file: update_riff_header(chan,siz); break;
    default:
      {
	printf("cannot update this header!");
	fflush(stdout);
	break;
      }
    }
}

int c_update_header (char *name, int type, int siz, int srate, int format, int chans, int loc)
{
  int chan;
  chan = clm_reopen_write(name);
  if (chan == -1) return(-1);
  c_update_header_with_fd(chan,type,siz);
  if (type == NeXT_sound_file)
    {
      if (srate != 0)
	{
	  lseek(chan,16L,0);
	  (*((int *)(hdrbuf))) = clm_int(srate);
	  write(chan,hdrbuf,4);
	}
      if (format != 0)
	{
	  lseek(chan,12L,0);
	  (*((int *)(hdrbuf))) = clm_int(format);
	  write(chan,hdrbuf,4);
	}
      if (chans != 0)
	{
	  lseek(chan,20L,0);
	  (*((int *)(hdrbuf))) = clm_int(chans);
	  write(chan,hdrbuf,4);
	}
      if (loc != 0)
	{
	  lseek(chan,4L,0);
	  (*((int *)(hdrbuf))) = clm_int(loc);
	  write(chan,hdrbuf,4);
	}
    }
  close(chan);
  return(0);
}


/* ------------------------------------------------------------------------
 * old Mus10, SAM formats, just for completeness
 *
 * These were used for sound data on the PDP-10s at SAIL and CCRMA in the 70's and 80's.
 * The word length was 36-bits.
 *
 * "New" format as used by nearly all CCRMA software pre-1990:
 *
 *  WD 0 - '525252525252 (chosen because "it is unlikely to occur at random"!)
 *  WD 1 - Clock rate in Hz (PDP-10 36-bit floating point) -- see trans.lisp for details
 *         PDP-10 floating point format was sign in bit 0, excess 128 exponent in 1-8, fraction in 9-35
 *  WD 2 - #samples per word,,pack-code, (has # samples per word in LH, pack-code in RH)
 * 	0 for 12-bit fixed point (this was the main one)
 * 	1 for 18-bit fixed point
 * 	2 for  9-bit floating point incremental (never used)
 * 	3 for 36-bit floating point (never used)
 * 	4 for 16-bit sambox fixed point, right justified
 * 	5 for 20-bit sambox fixed point
 * 	6 for 20-bit right-adjusted fixed point (sambox SAT format=SAM output)
 * 	7 for 16-bit fixed point, left justified (never used)
 * 	N>9 for N bit bytes in ILDB format (never used)
 *  WD 3 - # channels
 *  WD 4 - Maximum amplitude (if known) (PDP-10 float)
 *  WD 5 - number of Sambox ticks per pass (inverse of Sambox clock rate, sort of)
 *  WD 6 - Total #samples in file. If 0 then #wds_in_file*#samps_per_wd assumed.
 *  WD 7 - Block size (if any). 0 means sound is not blocked.
 *  WDs '10-'77 Reserved for EDSND usage, but I used it anyway.
 *  WDs '100-'177 Text description of file (in ASCIZ format) (ASCIZ=ASCII+null if I remember right = C string(?))
 *
 * "Old" format (pre-1977)
 *
 *  WD 0 - '525252525252
 *  WD 1 - Clock rate => rate as integer,,code
 * 	code=0 for 6.4Kc (or anything else)
 * 	    =1 for 12.8Kc, =2 for 25.6Kc, =3 for 51.2Kc
 * 	    =5 for 102.4Kc, =6 for 204.8Kc
 *  WD 2 - #sample per word,,pack
 * 	0 for 12 bit
 * 	1 for 16 bit (18 bit)
 * 	2 for 9 bit floating point incremental
 * 	3 for 36-bit floating point
 * 	N>9 for N bit bytes in ILDB format
 *  WD 3 - # channels
 *  WD 4 - Maximum amplitude (if known, otherwise 0)
 *  WDs 5-77 Reserved for future expansion
 *  WDs 100-177 Text description of file (in ASCIZ format) 
 *
 * ------------------------------------
 * and even more esoteric... from the MIT PDP-6 (1971-76):
 *      JRST 1
 *      0
 *      blkcnt,,2000
 *	duration,, ---    ; negative halfword number to be counted down for duration
 *	v0 v1 v2 v3 v4 v5 ; 6 six-bit byes, each encoding pitch no. from 2.-63, 63 not used
 *	duration,, --
 *	v0 v1 v2 v3 v4 v5 
 *	...
 *	checksum
 *      ...
 *	blkcnt,,blkaddr   ; neg half word in block, not counting itself or checksum,,addr of block start
 *	...
 *	0,,--
 *	0
 *      checksum          ;=ROT 1 and ADD including blkcnt,,blkaddr
 *	-1,,41
 *	JRST 101
 *	checksum
 *	-1,,41
 *	JRST 101
 *	checksum
 *	JUMPA 101
 *	JUMPA 101
 *
 * ah, the good old days...
 */

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.