#ifndef LINT
#ifdef RCS_ID
static char *rcsid = "@(#)$Header: /home/wesw/nwd/terminator/src/av/crl/audio/AF/lib/AFSox/RCS/8svx.c,v 1.5 1994/02/02 01:22:51 marvinw Exp $";
#endif /* RCS_ID */
#endif /* LINT */
/***********************************************************
$Copyright$,1994 by Tektronix, Inc., Wilsonville, Oregon.

                        All Rights Reserved

Permission to use, copy, modify, and distribute this software and its 
documentation for any purpose and without fee is hereby granted, 
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in 
supporting documentation, and that the names of Tektronix or Tek not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.  

TEKTRONIX DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
TEKTRONIX 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.

******************************************************************/
/*
 * Amiga 8SVX format handler: W V Neisius, February 1992
 */

#if	defined(unix) || defined(__OS2__)
#include <sys/types.h>
#endif
#include <math.h>
#include <errno.h>
#ifdef	VMS
#include <perror.h>
#endif
#include "st.h"

/* Private data used by writer */
struct svxpriv {
        unsigned long nsamples;
	FILE *ch[4];
};

#ifndef SEEK_CUR
#define SEEK_CUR        1
#endif
#ifndef SEEK_SET
#define SEEK_SET        0
#endif

/*======================================================================*/
/*                         8SVXSTARTREAD                                */
/*======================================================================*/

int
svxstartread(ft)
ft_t ft;
{
	struct svxpriv *p = (struct svxpriv *) ft->priv;

	char buf[12];
	char *endptr;
	char *chunk_buf;

	unsigned long totalsize;
	unsigned long chunksize;

	int channels;
	long rate;
	int littlendian = 0;
	int i;

	unsigned long chan1_pos;
	extern FILE *fopen_local_client_file(char *, char *);

	rate = 0;
	channels = 1;

	/* read FORM chunk */
	if (fread(buf, 1, 4, ft->fp) != 4 || strncmp(buf, "FORM", 4) != 0)
		return fail(ft, "8SVX: header does not begin with magic word 'FORM'");
	totalsize = rblong(ft);
	if (fread(buf, 1, 4, ft->fp) != 4 || strncmp(buf, "8SVX", 4) != 0)
		return fail(ft, "8SVX: 'FORM' chunk does not specify '8SVX' as type");

	/* read chunks until 'BODY' (or end) */
	while (fread(buf,1,4,ft->fp) == 4 && strncmp(buf,"BODY",4) != 0) {
		if (strncmp(buf,"VHDR",4) == 0) {
			chunksize = rblong(ft);
			if (chunksize != 20)
				return fail (ft, "8SVX: VHDR chunk has bad size");
			fseek(ft->fp,12,SEEK_CUR);
			rate = rbshort(ft);
			fseek(ft->fp,1,SEEK_CUR);
			fread(buf,1,1,ft->fp);
			if (buf[0] != 0)
				return fail (ft, "8SVX: unsupported data compression");
			fseek(ft->fp,4,SEEK_CUR);
			continue;
		}

		if (strncmp(buf,"ANNO",4) == 0) {
			chunksize = rblong(ft);
			if (chunksize & 1)
				chunksize++;
			chunk_buf = (char *) malloc(chunksize + 1);
			if (fread(chunk_buf,1,(size_t)chunksize,ft->fp) 
					!= chunksize)
				return fail(ft, "8SVX: Unexpected EOF in ANNO header");
			chunk_buf[chunksize] = '\0';
			report ("%s",chunk_buf);
			free(chunk_buf);

			continue;
		}

		if (strncmp(buf,"NAME",4) == 0) {
			chunksize = rblong(ft);
			if (chunksize & 1)
				chunksize++;
			chunk_buf = (char *) malloc(chunksize + 1);
			if (fread (chunk_buf,1,(size_t)chunksize,ft->fp) 
					!= chunksize)
				return fail(ft, "8SVX: Unexpected EOF in NAME header");
			chunk_buf[chunksize] = '\0';
			report ("%s",chunk_buf);
			free(chunk_buf);

			continue;
		}

		if (strncmp(buf,"CHAN",4) == 0) {
			chunksize = rblong(ft);
			if (chunksize != 4) 
				return fail(ft, "8SVX: Short channel chunk");
			channels = rblong(ft);
			channels = (channels & 0x01) + 
					((channels & 0x02) >> 1) +
				   	((channels & 0x04) >> 2) + 
					((channels & 0x08) >> 3);

			continue;
		}

		/* some other kind of chunk */
		chunksize = rblong(ft);
		if (chunksize & 1)
			chunksize++;
		fseek(ft->fp,chunksize,SEEK_CUR);
		continue;

	}

	if (rate == 0)
		return fail (ft, "8SVX: invalid rate");
	if (strncmp(buf,"BODY",4) != 0)
		return fail (ft, "8SVX: BODY chunk not found");
	p->nsamples = rblong(ft);

	ft->info.channels = channels;
	ft->info.rate = rate;
	ft->info.style = SIGN2;
	ft->info.size = BYTE;

	/* open files to channels */
	p->ch[0] = ft->fp;
	chan1_pos = ftell(p->ch[0]);

	for (i = 1; i < channels; i++) {
		if ((p->ch[i] = fopen_local_client_file(ft->filename, READBINARY)) == NULL)
			return fail(ft, "Can't open channel file '%s': %s",
				ft->filename, strerror(errno));

		/* position channel files */
		if (fseek(p->ch[i],chan1_pos,SEEK_SET))
		    return fail (ft, "Can't position channel %d: %s",i,strerror(errno));
		if (fseek(p->ch[i],p->nsamples/channels*i,SEEK_CUR))
		    return fail (ft, "Can't seek channel %d: %s",i,strerror(errno));
	}


	endptr = (char *) &littlendian;
	*endptr = 1;
	if (littlendian == 1)
		ft->swap = 1;

	return AF_SUCCESS;
}

/*======================================================================*/
/*                         8SVXREAD                                     */
/*======================================================================*/
svxread(ft, buf, nsamp) 
ft_t ft;
long *buf, nsamp;
{
	register unsigned long datum;
	int done = 0;
	int i;

	struct svxpriv *p = (struct svxpriv *) ft->priv;

	while (done < nsamp) {
		for (i = 0; i < ft->info.channels; i++) {
			datum = getc(p->ch[i]);
			if (feof(p->ch[i]))
				return done;
			/* scale signed up to long's range */
			*buf++ = LEFT(datum, 24);
		}
		done += ft->info.channels;
	}
	return done;
}

/*======================================================================*/
/*                         8SVXSTOPREAD                                 */
/*======================================================================*/
svxstopread(ft)
ft_t ft;
{
	int i;

	struct svxpriv *p = (struct svxpriv *) ft->priv;

	/* close channel files */
	for (i = 1; i < ft->info.channels; i++) {
		fclose (p->ch[i]);
	}
}

/*======================================================================*/
/*                         8SVXSTARTWRITE                               */
/*======================================================================*/
svxstartwrite(ft)
ft_t ft;
{
	struct svxpriv *p = (struct svxpriv *) ft->priv;
	int littlendian = 0;
	int i;
	char *endptr;
	FILE *tmpfile();

	/* open channel output files */
	p->ch[0] = ft->fp;
	for (i = 1; i < ft->info.channels; i++) {
		if ((p->ch[i] = tmpfile()) == NULL)
			return fail(ft, "Can't open channel output file: %s",
				strerror(errno));
	}

	/* write header (channel 0) */
	ft->info.style = SIGN2;
	ft->info.size = BYTE;

	p->nsamples = 0;
	svxwriteheader(ft, p->nsamples);

	endptr = (char *) &littlendian;
	*endptr = 1;
	if (littlendian == 1)
		ft->swap = 1;
}

/*======================================================================*/
/*                         8SVXWRITE                                    */
/*======================================================================*/

svxwrite(ft, buf, len)
ft_t ft;
long *buf, len;
{
	struct svxpriv *p = (struct svxpriv *) ft->priv;

	register long datum;
	int done = 0;
	int i;

	p->nsamples += len;

	while(done < len) {
		for (i = 0; i < ft->info.channels; i++) {
			datum = RIGHT(*buf++, 24);
			putc((int)datum, p->ch[i]);
		}
		done += ft->info.channels;
	}
}

/*======================================================================*/
/*                         8SVXSTOPWRITE                                */
/*======================================================================*/

svxstopwrite(ft)
ft_t ft;
{
	struct svxpriv *p = (struct svxpriv *) ft->priv;

	int i;
	int len;
	char svxbuf[512];

	/* append all channel pieces to channel 0 */
	/* close temp files */
	for (i = 1; i < ft->info.channels; i++) {
		if (fseek (p->ch[i], 0L, 0))
			return fail (ft, "Can't rewind channel output file %d",i);
		while (!feof(p->ch[i])) {
			len = fread (svxbuf, 1, 512, p->ch[i]);
			fwrite (svxbuf, 1, len, p->ch[0]);
		}
		fclose (p->ch[i]);
	}

	/* fixup file sizes in header */
	if (fseek(ft->fp, 0L, 0) != 0)
		return fail(ft, "can't rewind output file to rewrite 8SVX header");
	svxwriteheader(ft, p->nsamples);
}

/*======================================================================*/
/*                         8SVXWRITEHEADER                              */
/*======================================================================*/
#define SVXHEADERSIZE 100
svxwriteheader(ft,nsamples)
ft_t ft;
long nsamples;
{
	fputs ("FORM", ft->fp);
	wblong(ft, nsamples + SVXHEADERSIZE - 8);  /* size of file */
	fputs("8SVX", ft->fp); /* File type */

	fputs ("VHDR", ft->fp);
	wblong(ft, (long) 20); /* number of bytes to follow */
	wblong(ft, nsamples);  /* samples, 1-shot */
	wblong(ft, (long) 0);  /* samples, repeat */
	wblong(ft, (long) 0);  /* samples per repeat cycle */
	wbshort(ft, (int) ft->info.rate); /* samples per second */
	fputc(1,ft->fp); /* number of octaves */
	fputc(0,ft->fp); /* data compression (none) */
	wbshort(ft,1); wbshort(ft,0); /* volume */

	fputs ("ANNO", ft->fp);
	wblong(ft, (long) 32); /* length of block */
	fputs ("File created by Sound Exchange  ", ft->fp);

	fputs ("CHAN", ft->fp);
	wblong(ft, (long) 4);
	wblong(ft, (ft->info.channels == 2) ? (long) 6 :
		   (ft->info.channels == 4) ? (long) 15 : (long) 2);

	fputs ("BODY", ft->fp);
	wblong(ft, nsamples); /* samples in file */
}
