/*************************************************************************/
/*                                                                       */
/*                Centre for Speech Technology Research                  */
/*                     University of Edinburgh, UK                       */
/*                         Copyright (c) 1996                            */
/*                        All Rights Reserved.                           */
/*                                                                       */
/*  Permission is hereby granted, free of charge, to use and distribute  */
/*  this software and its documentation without restriction, including   */
/*  without limitation the rights to use, copy, modify, merge, publish,  */
/*  distribute, sublicense, and/or sell copies of this work, and to      */
/*  permit persons to whom this work is furnished to do so, subject to   */
/*  the following conditions:                                            */
/*   1. The code must retain the above copyright notice, this list of    */
/*      conditions and the following disclaimer.                         */
/*   2. Any modifications must be clearly marked as such.                */
/*   3. Original authors' names are not deleted.                         */
/*   4. The authors' names are not used to endorse or promote products   */
/*      derived from this software without specific prior written        */
/*      permission.                                                      */
/*                                                                       */
/*  THE UNIVERSITY OF EDINBURGH AND THE CONTRIBUTORS TO THIS WORK        */
/*  DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING      */
/*  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT   */
/*  SHALL THE UNIVERSITY OF EDINBURGH NOR THE CONTRIBUTORS BE LIABLE     */
/*  FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES    */
/*  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN   */
/*  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,          */
/*  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF       */
/*  THIS SOFTWARE.                                                       */
/*                                                                       */
/*************************************************************************/
/*                  Author :  Alan Black and Paul Taylor                 */
/*                  Date   :  June 1996                                  */
/*-----------------------------------------------------------------------*/
/*   File I/O functions specific to various file formats                 */
/*                                                                       */
/*   Note that internally data will always be shorts and                 */
/*   native byte order, conversions to/from other byte formats           */
/*   or encodings happend at read/write time                             */
/*                                                                       */
/*=======================================================================*/
#include <stdlib.h> 
#include <stdio.h>
#include "EST_unix.h"
#include <string.h>
#include "EST_wave_aux.h"
#include "EST_wave_utils.h"
#include "EST_strcasecmp.h"
#include "waveP.h"
#include "EST_FileType.h"

static int def_load_sample_rate = 16000;

/*************************************************************************/
/*                                                                       */
/* Functions specific for each file format                               */
/*                                                                       */
/*************************************************************************/

/*=======================================================================*/
/* Sphere Nist files                                                     */
/*=======================================================================*/

static const char *NIST_SIG = "NIST_1A\n   1024\n";
static const char *NIST_END_SIG = "end_head\n";
#define NIST_HDR_SIZE 1024

int nist_get_param_int(const char *hdr, const char *field, int def_val)
{
    char *p;
    int val;

    if (((p=strstr(hdr,field)) != NULL) &&
	(strncmp(" -i ",p+strlen(field),4) == 0))
    {
	sscanf(p+strlen(field)+4,"%d",&val);
	return val;
    }
    else
	return def_val;

}

char *nist_get_param_str(const char *hdr, const char *field, const char *def_val)
{
    char *p,*val;
    int size;

    if (((p=strstr(hdr,field)) != NULL) &&
	(strncmp(" -s",p+strlen(field),3) == 0))
    {
	sscanf(p+strlen(field)+3,"%d",&size);
	val = walloc(char,size+1);
	/* Hmm don't know how long the %d is so do it again */
	sscanf(p+strlen(field)+3,"%d %s",&size,val);
	return val;
    }
    else
	return wstrdup(def_val);


}

const char *sample_type_to_nist(enum EST_sample_type_t sample_type)
{
    const char *c;
    switch (sample_type) {
    case st_unknown:  
	c = ""; break;
    case st_schar:  
	c = "PCM-1"; break;
    case st_mulaw:
	c = "ULAW"; break;
    case st_short: 
	c = "pcm"; break;
    case st_int:   
	c = "PCM-4"; break;
    case st_float:
	c = "REAL"; break;
      case st_double:
	c = "REAL"; break;
    default:
	fprintf(stderr,"Unknown sample type for nist");
	c = "";
    }
    return c;
}

enum EST_sample_type_t nist_to_sample_type(char *type)
{
    if ((streq(type,"pcm")) ||
	(streq(type,"PCM")) ||
	(streq(type,"pcm-2")))
	return st_short;
    if (strcmp(type,"pcm,embedded-shorten-v1.1") == 0)
	return st_shorten;
    else if ((EST_strcasecmp(type,"ULAW",NULL) == 0) ||
	     (EST_strcasecmp(type,"U-LAW",NULL) == 0) ||
	     (EST_strcasecmp(type,"mu-law",NULL) == 0) ||
	     (EST_strcasecmp(type,"mulaw",NULL) == 0))
	return st_mulaw;
    else if (strcmp(type,"PCM-1") == 0)
	return st_schar;
    else if (strcmp(type,"PCM-4") == 0)
	return st_int;
    else if (strcmp(type,"REAL") == 0)
	return st_float;
    else

    {
	fprintf(stderr,"NIST: unknown sample type: %s\n",type);
	return st_unknown;
    }
}

enum EST_read_status load_wave_nist(EST_TokenStream &ts, short **data, int
		       *num_samples, int *num_channels, int
		       *word_size, int *sample_rate, enum
		       EST_sample_type_t *sample_type, int *bo , int
		       offset, int length)

{
    char header[NIST_HDR_SIZE];
    int samps,sample_width,data_length,actual_bo;
    unsigned char *file_data;
    enum EST_sample_type_t actual_sample_type;
    char *byte_order, *sample_coding;
    int n;
    int current_pos;

    current_pos = ts.tell();
    if (ts.fread(header,NIST_HDR_SIZE,1) != 1)
	return misc_read_error;

    if (strncmp(header,NIST_SIG,sizeof(NIST_SIG)) != 0)
	return wrong_format;

    samps = nist_get_param_int(header,"sample_count",-1);
    *num_channels = nist_get_param_int(header,"channel_count",1);
    sample_width = nist_get_param_int(header,"sample_n_bytes",2);
    *sample_rate = 
	nist_get_param_int(header,"sample_rate",def_load_sample_rate);
    byte_order = nist_get_param_str(header,"sample_byte_format",
				    (EST_BIG_ENDIAN ? "10" : "01"));
    sample_coding = nist_get_param_str(header,"sample_coding","pcm");
    if (streq(byte_order,"mu-law"))
    {
	byte_order = wstrdup((EST_BIG_ENDIAN ? "10" : "01"));
	sample_coding = wstrdup("ULAW");
    }

    /* code for reading in Tony Robinson's shorten files.
       This is a temporary fix which calls the unshorten program on the
       speech file and reads in the answer by calling this function.
       It would be nice to have a simple library routine which did the
       unshortening.
       */
    
    if (streq(sample_coding,"pcm,embedded-shorten-v1.1"))
    {
	char *tmpfile, *cmdstr;
	enum EST_read_status rval;

	tmpfile = cmake_tmp_filename();
	cmdstr = walloc(char,strlen(tmpfile)+200);
	sprintf(cmdstr,"cstrshorten %s %s",
		(const char*)ts.filename(),tmpfile);
	printf("Command: %s\n", cmdstr);
	system(cmdstr);
	EST_TokenStream tt;
	tt.open(tmpfile);
	
	rval = load_wave_nist(tt, data, num_samples,
			      num_channels, word_size, sample_rate,
			      sample_type, bo, offset, length);
	unlink(tmpfile);
	wfree(tmpfile);
	wfree(cmdstr);
	tt.close();
	return rval;
    }

    if (length == 0)
	data_length = (samps - offset)*(*num_channels);
    else
	data_length = length*(*num_channels);

    file_data = walloc(unsigned char,sample_width * data_length);

    ts.seek(current_pos+NIST_HDR_SIZE+(sample_width*offset*(*num_channels)));

    n = ts.fread(file_data,sample_width,data_length);

    if ((n < 1) && (n != data_length))
    {
	wfree(file_data); 
	wfree(sample_coding);
	wfree(byte_order);
	return misc_read_error;
    }
    else if ((n < data_length) && (data_length/(*num_channels) == n))
    {
	fprintf(stderr,"WAVE read: nist header is (probably) non-standard\n");
	fprintf(stderr,"WAVE read: assuming different num_channel interpretation\n");
	data_length = n;   /* wrongly headered file */
    }
    else if (n < data_length)
    {
	fprintf(stderr,"WAVE read: short file %s\n",
		(const char *)ts.filename());
	fprintf(stderr,"WAVE read: at %d got %d instead of %d samples\n",
		offset,n,data_length);
	data_length = n;
    }

    actual_sample_type = nist_to_sample_type(sample_coding);
    actual_bo = ((strcmp(byte_order,"10") == 0) ? bo_big : bo_little);

    *data = convert_raw_data(file_data,data_length,
			     actual_sample_type,actual_bo);

    *num_samples = data_length/ (*num_channels);
    *sample_type = st_short;
    *bo = EST_NATIVE_BO;
    *word_size = 2;
    wfree(sample_coding);
    wfree(byte_order);

    return format_ok;
}

enum EST_write_status save_wave_nist(FILE *fp, const short *data, int offset,
				     int num_samples, int num_channels, 
				     int sample_rate,
				     enum EST_sample_type_t sample_type, int bo)   
{
    char h[1024], p[1024];
    const char *t;
    
    memset(h,0,1024);
    
    strcat(h, NIST_SIG);
    sprintf(p, "channel_count -i %d\n", num_channels);
    strcat(h, p);
    sprintf(p, "sample_count -i %d\n", num_samples);	
    strcat(h, p);
    sprintf(p, "sample_rate -i %d\n", sample_rate);	
    strcat(h, p);
    
    t = sample_type_to_nist(sample_type);
    if (t)
    {
	sprintf(p, "sample_coding -s%d %s\n", (signed)strlen(t), t);  
	strcat(h, p);
	sprintf(p, "sample_n_bytes -i %d\n", get_word_size(sample_type));
	strcat(h, p);
    }
    
    if (get_word_size(sample_type) > 1)
    {
	sprintf(p, "sample_byte_format -s%d %s\n", 2, 
		((bo == bo_big) ? "10" : "01"));
	strcat(h, p);
    }
    
    strcat(h, NIST_END_SIG);
    /*makes it nice to read */
    strcat(h, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); 
    
    if (fwrite(&h, 1024, 1, fp) != 1)
	return misc_write_error;
    
    return save_raw_data(fp,data,offset,num_samples,num_channels,
			 sample_type,bo);
    
}

/*=======================================================================*/
/* EST's own format                                                      */
/*=======================================================================*/

enum EST_read_status load_wave_est(EST_TokenStream &ts, short **data, int
				   *num_samples, int *num_channels, int
				   *word_size, int *sample_rate, enum
				   EST_sample_type_t *sample_type, int *bo, 
				   int offset, int length)
{
    int data_length, actual_bo;
    short *file_data;
    EST_String byte_order;
    int n;
    EST_EstFileType t;
    EST_Option hinfo;
    bool ascii;
    EST_read_status r;
    EST_sample_type_t actual_sample_type;
    
    offset = 0;
    
    if ((r = read_est_header(ts, hinfo, ascii, t)) != format_ok)
	return r;
    if (t != est_file_wave)
	return misc_read_error;
    
    *num_samples = hinfo.ival("NumSamples");
    *num_channels = hinfo.ival("NumChannels");
    *sample_rate = hinfo.ival("SampleRate");
    
    byte_order = hinfo.val("ByteOrder");
    
    if (length == 0)
	data_length = (*num_samples)*(*num_channels);
    else
	data_length = length*(*num_channels);
    
    file_data = walloc(short, data_length);
    
    n = ts.fread(file_data, sizeof(short), data_length);
    if ((n != data_length) && (n < 1))
    {
	cerr << "EST wave load: " << ts.pos_description() << endl;
	cerr << "failed to read file\n";
	wfree(file_data); 
	return misc_read_error;
    }
    else if (n != data_length)
    {
	cerr << "Wrong number of samples/channels in EST wave file\n";  
	cerr << ts.pos_description() << " ";
	cerr << "expected " << data_length << " got " << n << endl;
	data_length = n;
    }
    
    actual_bo = (byte_order == "10") ? bo_big : bo_little;
    if (hinfo.present("SampleType"))
	actual_sample_type = str_to_sample_type(hinfo.val("SampleType"));
    else
	actual_sample_type = st_short; // some older files don't have this
    
    *data = convert_raw_data((unsigned char *)file_data, 
			     data_length, actual_sample_type, actual_bo);
    // because internally data is always shorts
    *sample_type = st_short;
    *bo = EST_NATIVE_BO;
    *word_size = 2;
    
    return format_ok;
}

enum EST_write_status save_wave_est(FILE *fp, const short *data, int offset,
				       int num_samples, int num_channels, 
				       int sample_rate,
				       enum EST_sample_type_t sample_type, int bo)   
{
    fprintf(fp, "EST_File wave\n");
    fprintf(fp, "DataType binary\n");
    fprintf(fp, "SampleRate %d\n", sample_rate);
    fprintf(fp, "NumSamples %d\n", num_samples);
    fprintf(fp, "NumChannels %d\n", num_channels);
    fprintf(fp, "SampleType %s\n", sample_type_to_str(sample_type));
    if (get_word_size(sample_type) > 1)
	fprintf(fp, "ByteOrder %s\n", ((bo == bo_big) ? "10" : "01"));
    
    fprintf(fp, "EST_Header_End\n");
    
    return save_raw_data(fp, data, offset, num_samples, num_channels,
			 sample_type, bo);
    
}

/*=======================================================================*/
/*  Microsoft RIFF (.wav) audio files                                    */
/*                                                                       */
/*  The information on this format was gained by reading a document      */
/*  found on the net called "Multimedia Programming Interface and        */
/*  Data Specification v1.0" and by looking at Rick Richardson,          */
/*  Lance Norskog And Sundry Contributors code in SOX.  All this code    */
/*  is rewritten from scratch though, but I couldn't do it without       */
/*  other's explanations. I would have used the SOX code directly but    */
/*  was not really in the right form so starting again was easier        */
/*=======================================================================*/
#define WAVE_FORMAT_PCM    0x0001
#define WAVE_FORMAT_ADPCM  0x0002
#define WAVE_FORMAT_ALAW   0x0006
#define WAVE_FORMAT_MULAW  0x0007

enum EST_read_status load_wave_riff(EST_TokenStream &ts, short **data, int
				    *num_samples, int *num_channels, int
				    *word_size, int *sample_rate, enum
				    EST_sample_type_t *sample_type, int *bo , int
				    offset, int length)
{
    char info[4];
    int samps,sample_width,data_length;
    short shortdata;
    int dsize,intdata;
    unsigned char *file_data;
    enum EST_sample_type_t actual_sample_type;
    
    if (ts.fread(info,sizeof(char),4) != 4)
	return wrong_format;	/* its almost definitely an error */
    if (strncmp(info,"RIFF",4) != 0)
	return wrong_format;

    /* We've got a riff file */
    ts.fread(&dsize,4,1);
    /* .wav files are always little endian */
    if (EST_BIG_ENDIAN) dsize = SWAPINT(dsize);
    if ((ts.fread(info,sizeof(char),4) != 4) ||
	(strncmp(info,"WAVE",4) != 0))
    {
	fprintf(stderr, "RIFF file is not of type WAVE\n");
	return misc_read_error;	/* not a wave file */
    }
    if ((ts.fread(info,sizeof(char),4) != 4) ||
	(strncmp(info,"fmt ",4) != 0))
	return misc_read_error;	/* something else wrong */

    ts.fread(&dsize,4,1);
    if (EST_BIG_ENDIAN) dsize = SWAPINT(dsize);
    ts.fread(&shortdata,2,1);
    if (EST_BIG_ENDIAN) shortdata = SWAPSHORT(shortdata);

    switch (shortdata)
    {
	/* This is a non-proprietary format */
    case WAVE_FORMAT_PCM:
	actual_sample_type = st_short; break;
	/* The follow are registered proprietary WAVE formats  (?) */
    case WAVE_FORMAT_MULAW:
	actual_sample_type = st_mulaw; break;
    case WAVE_FORMAT_ADPCM:
	fprintf(stderr, "RIFF file: unsupported proprietary sample format ADPCM\n"); 
	actual_sample_type = st_short;
	break;
	/*	  actual_sample_type = st_adpcm; break; */ /* yes but which adpcm ! */
    case WAVE_FORMAT_ALAW:
    default:
	fprintf(stderr, "RIFF file: unknown sample format\n");
	actual_sample_type = st_short;
	/*	return misc_read_error; */
    }
    ts.fread(&shortdata,2,1);
    if (EST_BIG_ENDIAN) shortdata = SWAPSHORT(shortdata);
    *num_channels = shortdata;
    ts.fread(sample_rate,4,1);
    if (EST_BIG_ENDIAN) *sample_rate = SWAPINT(*sample_rate);
    ts.fread(&intdata,4,1);	/* average bytes per second -- ignored */
    if (EST_BIG_ENDIAN) intdata = SWAPINT(intdata);
    ts.fread(&shortdata,2,1);	/* block align ? */
    if (EST_BIG_ENDIAN) shortdata = SWAPSHORT(shortdata);
    ts.fread(&shortdata,2,1);
    if (EST_BIG_ENDIAN) shortdata = SWAPSHORT(shortdata);

    sample_width = (shortdata+7)/8;
    if ((sample_width == 1) && (actual_sample_type == st_short))
	actual_sample_type = st_uchar; /* oops I meant 8 bit */

    ts.seek((dsize-16)+ts.tell());     /* skip rest of header */
    while (1)
    {
	if (ts.fread(info,sizeof(char),4) != 4)
	{
	    fprintf(stderr,"RIFF file truncated\n");
	    return misc_read_error;	/* something else wrong */
	}
	if (strncmp(info,"data",4) == 0)
	{
	    ts.fread(&samps,4,1);
	    if (EST_BIG_ENDIAN) samps = SWAPINT(samps);
	    samps /= (sample_width*(*num_channels));
	    break;
	}
	else if (strncmp(info,"fact",4) == 0)
	{			/* some other type of chunk -- skip it */
	    ts.fread(&samps,4,1);
	    if (EST_BIG_ENDIAN) samps = SWAPINT(samps);
	    ts.seek(samps+ts.tell());	/* skip rest of header */
	    /* Hope this is the right amount */
	}
	else
	{
	    fprintf(stderr,"Unsupported chunk type \"%s\" in RIFF file\n",
		    info);
	    return misc_read_error;
	}
    }
    if (length == 0)
	data_length = (samps - offset)*(*num_channels);
    else
	data_length = length*(*num_channels);
    
    file_data = walloc(unsigned char,sample_width * data_length);
    
    ts.seek((sample_width*offset*(*num_channels))+ts.tell());
    if ((dsize=ts.fread(file_data,sample_width,data_length)) != data_length)
    {
	/*  It seems so many WAV files have their datasize wrong I'll */
	/*  let it through -- I think SOX is a major culprit          */
	if (length == 0) /* the file did the identification */
	    fprintf(stderr,"Unexpected end of file but continuing (apparently missing %d samples)\n",data_length-dsize);
	else
	{
	    fprintf(stderr,"Unexpected end of file: (missing %d samples)\n",data_length-dsize);
	    wfree(file_data);
	    return misc_read_error;
	}
    }
    
    *data = convert_raw_data(file_data,dsize,
			     actual_sample_type, bo_little);
    
    *num_samples = dsize / (*num_channels);
    *sample_type = st_short;
    *bo = EST_NATIVE_BO;
    *word_size = 2;
    
    return format_ok;
}

enum EST_write_status save_wave_riff(FILE *fp, const short *data, int offset,
				     int num_samples, int num_channels, 
				     int sample_rate,
				     enum EST_sample_type_t sample_type, int bo)   
{
    (void)bo;
    const char *info;
    int data_size, data_int;
    short data_short;

    if (sample_type == st_schar)
      {
	EST_warning("RIFF format: Signed 8-bit not allowed by this file format");
	sample_type=st_uchar; 
      }
    
    info = "RIFF"; fwrite(info,4,1,fp);
    data_size = num_channels*num_samples*get_word_size(sample_type)+ 8+16+12;
    /* WAV files are always LITTLE_ENDIAN (i.e. intel x86 format) */
    if (EST_BIG_ENDIAN) data_size = SWAPINT(data_size);
    fwrite(&data_size,1,4,fp);	/* total number of bytes in file */
    info = "WAVE"; fwrite(info,4,1,fp);
    info = "fmt "; fwrite(info,4,1,fp);
    data_size = 16;
    if (EST_BIG_ENDIAN) data_size = SWAPINT(data_size);
    fwrite(&data_size,1,4,fp);	/* size of header */
    switch (sample_type)
    {
    case st_short:  data_short = WAVE_FORMAT_PCM; break;
    case st_uchar: data_short = WAVE_FORMAT_PCM; break;
    case st_mulaw: data_short = WAVE_FORMAT_MULAW; break;
    case st_alaw: data_short = WAVE_FORMAT_ALAW; break;
    case st_adpcm: data_short = WAVE_FORMAT_ADPCM; break;
    default:
      fprintf(stderr,"RIFF format: unsupported data format %d\n",
	      sample_type);
      return misc_write_error;
	       }
    if (EST_BIG_ENDIAN) data_short = SWAPSHORT(data_short);
    fwrite(&data_short,1,2,fp);	/* sample type */
    data_short = num_channels;
    if (EST_BIG_ENDIAN) data_short = SWAPSHORT(data_short);
    fwrite(&data_short,1,2,fp);	/* number of channels */
    data_int = sample_rate;
    if (EST_BIG_ENDIAN) data_int = SWAPINT(data_int);
    fwrite(&data_int,1,4,fp);	/* sample rate */
    data_int = sample_rate * num_channels * get_word_size(sample_type);
    if (EST_BIG_ENDIAN) data_int = SWAPINT(data_int);
    fwrite(&data_int,1,4,fp);	/* Average bytes per second */
    data_short = num_channels * get_word_size(sample_type);
    if (EST_BIG_ENDIAN) data_short = SWAPSHORT(data_short);
    fwrite(&data_short,1,2,fp);	/* block align */
    data_short = get_word_size(sample_type) * 8;
    if (EST_BIG_ENDIAN) data_short = SWAPSHORT(data_short);
    fwrite(&data_short,1,2,fp);	/* bits per sample */
    info = "data"; fwrite(info,4,1,fp);
    data_size = num_channels*num_samples*get_word_size(sample_type);
    if (EST_BIG_ENDIAN) data_size = SWAPINT(data_size);
    fwrite(&data_size,1,4,fp);	/* total number of bytes in data */
    
    return save_raw_data(fp,data,offset,num_samples,num_channels,
			 sample_type,bo_little);
}

/*=======================================================================*/
/* Amiga/Apple AIFF waveform format                                      */
/* This was constructed using info in AudioIFF1.3.hqx found on the web   */
/* and also I did look at SOX's aiff.c written by Guido van Rossum       */
/* and Sundry Contributors.                                              */
/*=======================================================================*/

struct AIFFchunk {
    char id[4];
    int size;
};

struct AIFFssnd {		/* Sound Data Chunk */
    int offset;
    int blocksize;
};

enum EST_read_status load_wave_aiff(EST_TokenStream &ts, short **data, int
				    *num_samples, int *num_channels, int
				    *word_size, int *sample_rate, enum
				    EST_sample_type_t *sample_type, int *bo , int
				    offset, int length)
{
    char info[4];
    struct AIFFchunk chunk;
    short comm_channels;
    int comm_samples;
    short comm_bits;
    unsigned char ieee_ext_sample_rate[10];
    struct AIFFssnd ssndchunk;
    enum EST_sample_type_t actual_sample_type;
    int dsize,data_length,n;
    unsigned char *file_data;
    
    if (ts.fread(info,sizeof(char),4) != 4)
	return wrong_format;	/* but its almost definitely an error */
    if (strncmp(info,"FORM",4) != 0)
	return wrong_format;

    /* We've got an aiff file, I hope */
    ts.fread(&dsize,4,1);
    if (EST_LITTLE_ENDIAN)	/* file is in different byte order */
	dsize = SWAPINT(dsize);
    if ((ts.fread(info,sizeof(char),4) != 4) ||
	(strncmp(info,"AIFF",4) != 0))
    {
	fprintf(stderr, "AIFF file does not have AIFF chunk\n");
	return misc_read_error; 
    }
    
    for ( ; ts.fread(&chunk,1,sizeof(chunk)) == sizeof(chunk) ; )
    {				/* for each chunk in the file */
	if (EST_LITTLE_ENDIAN)	/* file is in different byte order */
	    chunk.size = SWAPINT(chunk.size);
	if (strncmp(chunk.id,"COMM",4) == 0)
	{
	    if (chunk.size != 18)
	    {
		fprintf(stderr,"AIFF chunk: bad size\n");
		return misc_read_error;
	    }
	    ts.fread(&comm_channels,1,sizeof(short));
	    ts.fread(&comm_samples,1,sizeof(int));
	    ts.fread(&comm_bits,1,sizeof(short));
	    if (ts.fread(ieee_ext_sample_rate,1,10) != 10)
	    {
		fprintf(stderr,"AIFF chunk: eof within COMM chunk\n");
		return misc_read_error;
	    }
	    if (EST_LITTLE_ENDIAN)
	    {
		comm_channels = SWAPSHORT(comm_channels);
		comm_samples = SWAPINT(comm_samples);
		comm_bits = SWAPSHORT(comm_bits);
	    }
	    *sample_rate = (int)ConvertFromIeeeExtended(ieee_ext_sample_rate);
	}
	else if (strncmp(chunk.id,"SSND",4) == 0)
	{
	    if (ts.fread(&ssndchunk,1,sizeof(ssndchunk)) != sizeof(ssndchunk))
	    {
		fprintf(stderr,"AIFF chunk: eof within SSND chunk\n");
		return misc_read_error;
	    }
	    if (EST_LITTLE_ENDIAN)
	    {
		ssndchunk.offset = SWAPINT(ssndchunk.offset);
		ssndchunk.blocksize = SWAPINT(ssndchunk.blocksize);
	    }
	    
	    *num_channels = comm_channels;
	    switch (comm_bits)
	    {
	    case 8: actual_sample_type = st_uchar; break;
		case 16: actual_sample_type = st_short; break;
		default:
		    fprintf(stderr,"AIFF: unsupported sample width %d bits\n",
			    comm_bits);
		    return misc_read_error;
		}
	    
	    ts.seek(ssndchunk.offset+(comm_channels*offset)+ts.tell());
	    if (length == 0)
 		data_length = (comm_samples-offset)*comm_channels;
  	    else
 		data_length = length*comm_channels;
	    file_data = walloc(unsigned char, 
			       data_length*comm_channels*
			       get_word_size(actual_sample_type));
	    if ((n=ts.fread(file_data,get_word_size(actual_sample_type),
			 data_length)) != data_length)
	    {
		fprintf(stderr,"AIFF read: short file %s\n",
			(const char *)ts.filename());
		fprintf(stderr,"AIFF read: at %d got %d instead of %d samples\n",
			offset,n,data_length);
		data_length = n;
	    }
	    
	    *data = convert_raw_data(file_data,data_length,
				     actual_sample_type,bo_big);
	    *num_samples = data_length/comm_channels;
	    *sample_type = st_short;
	    *word_size = 2;
	    *bo = EST_NATIVE_BO;
	    break;			/* only care about the first SSND chunk */
	}
	else 
	{			/* skip bytes in chunk */
	    ts.seek(ts.tell()+chunk.size);
	}
    }
    
    return format_ok;
}

enum EST_write_status save_wave_aiff(FILE *fp, const short *data, int offset,
				     int num_samples, int num_channels, 
				     int sample_rate,
				     enum EST_sample_type_t sample_type, int bo)   
{
    (void)bo;
    const char *info;
    int data_size, data_int;
    unsigned char ieee_ext_buf[10];
    short data_short;
    
    info = "FORM";
    fwrite(info,1,4,fp);
    /* This number seems to be derived different for each example */
    data_size = 54+(num_samples*num_channels*get_word_size(sample_type));
    if (EST_LITTLE_ENDIAN)
	data_size = SWAPINT(data_size);
    fwrite(&data_size,1,4,fp);
    info = "AIFF";
    fwrite(info,1,4,fp);
    info = "COMM";
    fwrite(info,1,4,fp);
    data_int = 18;
    if (EST_LITTLE_ENDIAN)
	data_int = SWAPINT(data_int);
    fwrite(&data_int,1,4,fp);
    data_short = num_channels;
    if (EST_LITTLE_ENDIAN)
	data_short = SWAPSHORT(data_short);
    fwrite(&data_short,1,2,fp);
    data_int = num_samples;
    if (EST_LITTLE_ENDIAN)
	data_int = SWAPINT(data_int);
    fwrite(&data_int,1,4,fp);
    data_short = 8*get_word_size(sample_type);
    if (EST_LITTLE_ENDIAN)
	data_short = SWAPSHORT(data_short);
    fwrite(&data_short,1,2,fp);
    ConvertToIeeeExtended((double)sample_rate,ieee_ext_buf);
    fwrite(ieee_ext_buf,1,10,fp);
    info = "SSND";
    fwrite(info,1,4,fp);
    data_int = 8 + (num_samples*num_channels*get_word_size(sample_type));
    if (EST_LITTLE_ENDIAN)
	data_int = SWAPINT(data_int);
    fwrite(&data_int,1,4,fp);
    data_int = 0;
    if (EST_LITTLE_ENDIAN)
	data_int = SWAPINT(data_int);
    fwrite(&data_int,1,4,fp);   /* offset */
    if (EST_LITTLE_ENDIAN)
	data_int = SWAPINT(data_int);
    fwrite(&data_int,1,4,fp);   /* blocksize */
    
    if ((sample_type == st_short) ||
	(sample_type == st_uchar))
	return save_raw_data(fp,data,offset,num_samples,num_channels,
			     sample_type,bo_big);
    else
    {
	fprintf(stderr,"AIFF: requested data type not uchar or short\n");
	return misc_write_error;
    }
    
}

/*=======================================================================*/
/* ulaw EST_filetype are just raw data with 8K ulaw contents                 */
/*=======================================================================*/

enum EST_read_status load_wave_ulaw(EST_TokenStream &ts, short **data, int
				    *num_samples, int *num_channels, int *word_size, int
				    *sample_rate, enum EST_sample_type_t *sample_type, int *bo,
				    int offset, int length)

{
    unsigned char *ulaw;
    int data_length,samps;
    
    ts.seek_end();
    samps = ts.tell();
    
    if (length == 0)
	data_length = samps - offset;
    else
	data_length = length;
    
    ulaw = walloc(unsigned char, data_length);
    ts.seek(offset);
    if (ts.fread(ulaw,1,data_length) != data_length)
    {
	wfree(ulaw); 
	return misc_read_error;
    }
    
    *data = walloc(short,data_length);
    ulaw_to_short(ulaw,*data,data_length);
    wfree(ulaw);
    
    *num_samples = data_length;
    *sample_rate = 8000;
    *num_channels = 1;
    *sample_type = st_short;
    *word_size = 2;
    *bo = EST_NATIVE_BO;
    
    return format_ok;
}

enum EST_write_status save_wave_ulaw(FILE *fp, const short *data, int offset,
				     int num_samples, int num_channels, 
				     int sample_rate, 
				     enum EST_sample_type_t sample_type, int bo)
{
    (void)sample_rate;
    (void)sample_type;
    return save_wave_raw(fp,data,offset,num_samples,num_channels,
			 8000,st_mulaw,bo);
    
    
}

/*=======================================================================*/
/* Sun and Next snd files                                                */
/*=======================================================================*/

typedef struct {
    unsigned int    magic;	/* magic number */
    unsigned int    hdr_size;	/* size of this header */
    int    data_size;	        /* length of data (optional) */
    unsigned int    encoding;	/* data encoding format */
    unsigned int    sample_rate; /* samples per second */
    unsigned int    channels;	 /* number of interleaved channels */
} Sun_au_header;

enum EST_read_status load_wave_snd(EST_TokenStream &ts, short **data, int
				   *num_samples, int *num_channels, int *word_size, int
				   *sample_rate,enum EST_sample_type_t *sample_type, int *bo ,
				   int offset, int length)
{
    /* Header structures */
    Sun_au_header header;
    enum EST_sample_type_t encoding_type;
    int data_length, sample_width, bytes, samps, n;
    unsigned char *file_data;
    int current_pos;
    
    current_pos = ts.tell();
    ts.fread(&header, sizeof(Sun_au_header), 1);
    
    /* test for magic number */
    if ((EST_LITTLE_ENDIAN) && 
	((unsigned int)0x2e736e64 == SWAPINT(header.magic)))
    {				/* wrong byte order, swap header */
	header.hdr_size = SWAPINT(header.hdr_size);
	header.data_size = SWAPINT(header.data_size);
	header.encoding = SWAPINT(header.encoding);
	header.sample_rate = SWAPINT(header.sample_rate);
	header.channels = SWAPINT(header.channels);
    }
    else if ((unsigned int)0x2e736e64 != header.magic)
	return wrong_format;
    
    switch (header.encoding) 
    {
    case 1:
	encoding_type = st_mulaw;	
	break;
    case 2:
	encoding_type = st_uchar;	
	break;
    case 3:
	encoding_type = st_short;	
	break;
    default:
	fprintf(stderr, "Unsupported data type in SND header\n");
	return misc_read_error;
    }
    
    *num_channels = header.channels;
    sample_width = get_word_size(encoding_type);
    *sample_rate = header.sample_rate;
    
    if ((header.data_size == 0) ||
	(header.data_size == -1))
    {
	ts.seek_end();
	bytes = ts.tell() - header.hdr_size;
    }
    else
	bytes = header.data_size;
    samps = bytes/sample_width;
    
    if (length == 0)
	data_length = (samps - offset)*(*num_channels);
    else
	data_length = length *(*num_channels);
    
    file_data = walloc(unsigned char, sample_width * data_length);
    ts.seek(current_pos+header.hdr_size+(sample_width*offset*(*num_channels)));
    if ((n=ts.fread(file_data,sample_width,data_length)) != data_length)
    {
	fprintf(stderr,"WAVE read: short file %s\n",
		(const char *)ts.filename());
	fprintf(stderr,"WAVE read: at %d got %d instead of %d samples\n",
		offset,n,data_length);
	data_length = n;
    }
    
    *data = convert_raw_data(file_data,data_length,encoding_type,bo_big);

    if (*data == NULL)
      return read_error;
    
    *num_samples = data_length/ (*num_channels);
    *sample_type = st_short;
    *bo = EST_NATIVE_BO;
    *word_size = 2;
    return read_ok;
}

enum EST_write_status save_wave_snd(FILE *fp, const short *data, int offset,
				    int num_samples, int num_channels, 
				    int sample_rate, 
				    enum EST_sample_type_t sample_type, int bo)
{
    (void)bo;
    /* Header structures */
    Sun_au_header header;
    
    /* Fill in header structure */
    header.magic = (unsigned int)0x2e736e64; /* should be a macro surely */
    header.hdr_size = sizeof(header);	     /* ! */
    header.data_size = get_word_size(sample_type) * num_channels * num_samples;
    
    switch (sample_type) {
    case st_mulaw:
	header.encoding = 1;
	break;
    case st_uchar:
	header.encoding = 2;
	break;
    case st_short:
	header.encoding = 3;
	break;
	
    default:
	fprintf(stderr, 
		"Unsupported sample type cannot be saved in SND format\n");
	return misc_write_error;
	
    }
    
    /* check consistency */
    
    header.sample_rate = sample_rate;
    
    header.channels = num_channels;
    
    if (EST_LITTLE_ENDIAN)
    {
	/* snd files all in big endian format */
	header.magic = SWAPINT(header.magic);
	header.hdr_size = SWAPINT(header.hdr_size);
	header.data_size = SWAPINT(header.data_size);
	header.encoding = SWAPINT(header.encoding);
	header.sample_rate = SWAPINT(header.sample_rate);
	header.channels = SWAPINT(header.channels);
	
    }
    /* write header */
    if (fwrite(&header, sizeof(header), 1, fp) != 1)
	return misc_write_error;

    /* snd files are always in BIG_ENDIAN (sun) byte order */
    return save_raw_data(fp,data,offset,num_samples,num_channels,
			 sample_type,bo_big);
}

/*=======================================================================*/
/* CSTR Audlab files (from the last century)                             */
/* They are always bigendian                                             */
/*=======================================================================*/
struct s1 {
    char   c[17];
    float  f1;
    float  f2;
};

struct s2 {
    float f1;
    float f2;   
    float f3;
    char c1;
    char c2;
    int i1;
    int i2;
};

struct audlabfh {
    struct s1 z; 
    char  file_type[8];
    char  c1[17];
    char  c2[17];
    char  c3[17];
    int   start;
    char  data_type;    
    char  c5[64];
};

struct audlabsh {
    int   channel_count;
    char  serial;
    int   sample_rate;
    char  c1[20];
    int   i1;
    char  c2;
    char  c3[121];
    char  c4[121];
    
};
struct audlabsd {
    char descr[17];
    int sample_count;
    int nbits;
    float f1;
    struct s2 z;
};

enum EST_read_status load_wave_audlab(EST_TokenStream &ts, short **data, int
				      *num_samples, int *num_channels, int *word_size, int
				      *sample_rate, enum EST_sample_type_t *sample_type, int *bo, int
				      offset, int length)
{
    /* Header structures */
    struct audlabfh fh;
    struct audlabsh sh;
    struct audlabsd sd;
    int data_length,sample_count;
    int hdr_length;
    int current_pos;
    
    /* Read header structures from char array */
    current_pos = ts.tell();
    ts.fread(&fh, sizeof(struct audlabfh), 1);
    if (strcmp(fh.file_type, "Sample") != 0) 
	return wrong_format;
    
    ts.fread(&sh, sizeof(struct audlabsh), 1);
    ts.fread(&sd, sizeof(struct audlabsd), 1);
    hdr_length = sizeof(struct audlabfh) +
	sizeof(struct audlabsh) +
	    sizeof(struct audlabsd);
    
    if (EST_BIG_ENDIAN)
    {
	*num_channels = sh.channel_count;
	*sample_rate = sh.sample_rate;
	sample_count = sd.sample_count;
    }
    else			// audlab files are bigendian
    {
	*num_channels = SWAPINT(sh.channel_count);
	*sample_rate = SWAPINT(sh.sample_rate);
	sample_count = SWAPINT(sd.sample_count);
    }
    if (length == 0)
	data_length = (sample_count - offset) * (*num_channels);
    else
	data_length = length *(*num_channels);
    
    *data = walloc(short,sizeof(short) * data_length);
    ts.seek(current_pos+hdr_length+(sizeof(short)*offset*(*num_channels)));
    
    if ((int)ts.fread(*data, sizeof(short), data_length) != data_length)
    {
	wfree(*data);
	return misc_read_error;
    }
    if (EST_LITTLE_ENDIAN)
	swap_bytes_short(*data,data_length);
    
    *num_samples = data_length / (*num_channels);
    *sample_type = st_short;	/* set internal type*/
    *word_size = sizeof(short);
    *bo = EST_NATIVE_BO;
    
    return format_ok;
}

enum EST_write_status save_wave_audlab(FILE *fp, const short *data, int offset,
				       int num_samples, int num_channels, 
				       int sample_rate, 
				       enum EST_sample_type_t sample_type, int bo)
{
    (void)bo;
    (void)sample_type;
    /* Header structures */
    struct audlabfh fh;
    struct audlabsh sh;
    struct audlabsd sd;
    
    fh.start = sizeof (struct audlabfh) +
	sizeof (struct audlabsh) + sizeof (struct audlabsd);
    fh.data_type = 2;
    strcpy(fh.file_type, "Sample");
    
    if (EST_LITTLE_ENDIAN) 
    {				// need to swap some of those numbers 
	sh.channel_count = SWAPINT(num_channels);
	sh.serial = 1;
	sh.sample_rate = SWAPINT(sample_rate);
	
	sd.sample_count = SWAPINT(num_samples);
	sd.nbits = SWAPINT(16);
    }
    else
    {
	sh.channel_count = num_channels;
	sh.serial = 1;
	sh.sample_rate = sample_rate;
	
	sd.sample_count = num_samples;
	sd.nbits = 16;
    }
    sprintf(sd.descr, "Filter 1");
    
    /* write headers */
    fwrite (&fh, sizeof(fh), 1, fp);
    fwrite (&sh, sizeof(sh), 1, fp);
    fwrite (&sd, sizeof(sd), 1, fp);
    
    /* write data*/
    return save_raw_data(fp,data,offset,num_samples,num_channels,
			 st_short,bo_big);
}

/*=======================================================================*/
/* Entropic ESPS SD files: portable (non-proprietary) method             */
/*=======================================================================*/

/* Deep thanks go to Peter Kabal from McGill University whose AF code */
/* showed me this was even possible.  I looked at his code to find    */
/* parts I couldn't work out myself.  Also to Rodney Johnson of       */
/* Entropic whose document "ESPS APPLICATION NOTE: Non-ESPS Programs  */
/* and the ESPS File System" gave details of how to access ESPS files */
/* without using the ESPS library code.                               */

#include "esps_utils.h"
enum EST_read_status load_wave_sd(EST_TokenStream &ts, short **data, int
				  *num_samples, int *num_channels, int
				  *word_size, int *sample_rate, enum
				  EST_sample_type_t *sample_type, int *bo , int
				  offset, int length)
{
    /* A license free version of an esps file reading program */
    FILE *fd;
    esps_hdr hdr;
    int actual_bo, sample_width, data_length;
    enum EST_read_status rv;
    int dl;
    enum EST_sample_type_t actual_sample_type;
    double d;
    unsigned char *file_data;

    if ((fd = ts.filedescriptor()) == NULL)
    {
	fprintf(stderr, "Can't open esps file %s for reading\n",
		(const char *)ts.filename());
	return misc_read_error;
    }
    
    if ((rv=read_esps_hdr(&hdr,fd)) != format_ok)
	return rv;
    
    if (hdr->file_type != ESPS_SD)
    {
	fprintf(stderr,"ESPS file: not an FEA_SD file\n");
	delete_esps_hdr(hdr);
	return misc_read_error;
    }
    
    if (fea_value_d("record_freq",0,hdr,&d) != 0)
    {
	fprintf(stderr,"ESPS file: can't find sample_rate in header assuming 16000\n");
	*sample_rate = 16000;
    }
    else
	*sample_rate = (int)d;
    actual_sample_type = st_short; /* you're not trying hard enough */
    sample_width = get_word_size(actual_sample_type);
    *num_channels = hdr->field_dimension[0];
    if (hdr->swapped)
	actual_bo = (EST_BIG_ENDIAN ? bo_little : bo_big);
    else
	actual_bo = (EST_BIG_ENDIAN ? bo_big : bo_little);
    
    if (length == 0)
	data_length = (hdr->num_records - offset)*(*num_channels);
    else
	data_length = length *(*num_channels);
    
    file_data = walloc(unsigned char, sample_width * data_length);
    fseek(fd,hdr->hdr_size+(sample_width*offset*(*num_channels)),
	  SEEK_SET);
    if ((dl=fread(file_data,sample_width,data_length,fd)) != data_length)
    {
	fprintf(stderr,"WAVE read: esps short file %s\n",
		(const char *)ts.filename());
	fprintf(stderr,"WAVE read: at %d got %d instead of %d samples\n",
		offset,dl,data_length);
	data_length = dl;
    }
    
    *data = convert_raw_data(file_data,data_length,
			     actual_sample_type,
			     actual_bo);
    
    *num_samples = data_length/ (*num_channels);
    *sample_type = st_short;
    *bo = EST_NATIVE_BO;
    *word_size = 2;
    delete_esps_hdr(hdr);
    return format_ok;
    
}

enum EST_write_status save_wave_sd(FILE *fp, const short *data, int offset,
				   int num_samples, int num_channels, 
				   int sample_rate, 
				   enum EST_sample_type_t sample_type, int bo)

{
    (void)bo;
    esps_hdr hdr = make_esps_sd_hdr();
    enum EST_write_status rv;
    short esps_type;
    
    hdr->num_records = num_samples;
    switch (sample_type)
    {
    case st_short:  esps_type = ESPS_SHORT; break;
		case st_schar:  esps_type = ESPS_CHAR; break; /* maybe should be BYTE */
		case st_int:    esps_type = ESPS_INT; break;
		case st_float:  esps_type = ESPS_FLOAT; break;
		case st_double: esps_type = ESPS_DOUBLE; break;
		default:
		    fprintf(stderr,"ESPS file: no support for sample_type %s\n",
			    sample_type_to_str(sample_type));
		    return misc_write_error;
		}
    /* I believe all of the following are necessary and in this order */
    add_field(hdr,"samples",esps_type,num_channels);
    add_fea_special(hdr,ESPS_FEA_DIRECTORY,"margo:/disk/disk10/home/awb/projects/speech_tools/main");
    add_fea_special(hdr,ESPS_FEA_COMMAND,
		    "EDST waveform written as ESPS FEA_SD.\n\
			");
    add_fea_d(hdr,"start_time",0,(double)0);
    add_fea_d(hdr,"record_freq",0,(double)sample_rate);
    add_fea_d(hdr,"max_value",0,(double)27355);
    
    if ((rv=write_esps_hdr(hdr,fp)) != write_ok)
    {
	delete_esps_hdr(hdr);
	return rv;
    }
    /* lets ignore desired bo and sample type for the time being */
    delete_esps_hdr(hdr);
    
    return save_raw_data(fp,data,offset,num_samples,num_channels,
			 sample_type,EST_NATIVE_BO);
}

/*=======================================================================*/
/* Raw data files -- unheadered                                          */
/* THESE FUNCTIONS ARE DIFFERENT FROM THE REST                           */
/* These function have more parameters than the standard file i/o        */
/* as information cannot be gleamed from the file                        */
/*=======================================================================*/

enum EST_read_status load_wave_raw(EST_TokenStream &ts, short **data, int
				   *num_samples, int *num_channels, 
				   int *word_size, int
				   *sample_rate,  
				   enum EST_sample_type_t *sample_type, 
				   int *bo, int offset, int length, 
				   int isample_rate, 
				   enum EST_sample_type_t isample_type,
				   int ibo, int inc)
{
    unsigned char *file_data;
    int data_length,samps,sample_width;
    int guess,i,samp;
    short *ndata;
    
    if (isample_type == st_ascii)
    {
	/* Guess the size */
	if ((offset != 0) || (length != 0))
	{
	    fprintf(stderr,"Load ascii wave: doesn't support offets and lengths\n");
	    return misc_read_error;
	}
	
	ts.seek_end();
	guess = (int)(1.2*ts.tell()/7)+10; /* rough guess of the num of samps */
	ts.seek(0);
	*data = walloc(short, guess);
	i=0;
	while (!ts.eof())
	{
	    samp = atoi(ts.get().string());
	    if (i == guess)
	    {
		ndata = walloc(short,(int)(guess*1.2));
		memmove(ndata,*data,guess*sizeof(short));
		wfree(*data);
		*data = ndata;
		guess = (int)(guess*1.2);
	    }
	    if (samp < -32768)
	    {
		fprintf(stderr,"Load ascii wave: sample %d underflow clipping\n",
			i);
		(*data)[i] = -32768;
	    }
	    else if (samp > 32767)
	    {
		fprintf(stderr,"Load ascii wave: sample %d overflow clipping\n",
			i);
		(*data)[i] = 32767;
	    }
	    else
		(*data)[i] = (short)samp;
	    i++;
	}
	data_length = i;
    }
    else
    {
	ts.seek_end();
	sample_width = get_word_size(isample_type);
	samps = ts.tell()/sample_width;
	
	if (length == 0)
	    data_length = samps - offset;
	else
	    data_length = length;
	
	file_data = walloc(unsigned char, data_length * sample_width *inc);
	ts.seek(offset*sample_width*inc);
	if ((int)ts.fread(file_data,sample_width,data_length) != data_length)
	    return misc_read_error;
	
	*data = convert_raw_data(file_data,data_length,isample_type,ibo);
    }
    
    *num_samples = data_length/inc;
    *sample_rate = isample_rate;
    *num_channels = inc;
    *sample_type = st_short;
    *word_size = 2;
    *bo = EST_NATIVE_BO;
    
    return format_ok;
}

enum EST_write_status save_wave_raw(FILE *fp, const short *data, 
				    int offset,
				    int num_samples, int num_channels, 
				    int sample_rate,
				    enum EST_sample_type_t sample_type, int bo)   
{
    (void)sample_rate;
    
    return save_raw_data(fp,data,offset,num_samples,num_channels,
			 sample_type,bo);
}

/***********************************************************************/
/*                                                                     */
/* end of file type specific functions                                 */
/*                                                                     */
/***********************************************************************/

