shithub: sox

ref: 1e2e146fe7ef2b968ab1d3acadd743e031833ece
dir: /src/8svx.c/

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

#include <math.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#ifdef	VMS
#include <perror.h>
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>	/* For SEEK_* defines if not found in stdio */
#endif

#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif

#include "st.h"

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

void svxwriteheader(P2(ft_t, LONG));
    
/*======================================================================*/
/*                         8SVXSTARTREAD                                */
/*======================================================================*/

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

	char buf[12];
	char *chunk_buf;
 
	ULONG totalsize;
	ULONG chunksize;

	int channels;
	LONG rate;
	int i;
	int littlendian = 1;
	char *endptr;

	ULONG chan1_pos;

	endptr = (char*) &littlendian;
	/* 8svx is in big endian format. Swap whats
	 * read in on little endian machines.
	 */
	if (*endptr)
	{
		ft->swap = ft->swap ? 0 : 1;
	}

	rate = 0;
	channels = 1;

	/* read FORM chunk */
	if (fread(buf, 1, 4, ft->fp) != 4 || strncmp(buf, "FORM", 4) != 0)
		fail("8SVX: header does not begin with magic word 'FORM'");
	totalsize = rlong(ft);
	if (fread(buf, 1, 4, ft->fp) != 4 || strncmp(buf, "8SVX", 4) != 0)
		fail("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 = rlong(ft);
			if (chunksize != 20)
				fail ("8SVX: VHDR chunk has bad size");
			fseek(ft->fp,12,SEEK_CUR);
			rate = rshort(ft);
			fseek(ft->fp,1,SEEK_CUR);
			fread(buf,1,1,ft->fp);
			if (buf[0] != 0)
				fail ("8SVX: unsupported data compression");
			fseek(ft->fp,4,SEEK_CUR);
			continue;
		}

		if (strncmp(buf,"ANNO",4) == 0) {
			chunksize = rlong(ft);
			if (chunksize & 1)
				chunksize++;
			chunk_buf = (char *) malloc(chunksize + 1);
			if (fread(chunk_buf,1,(size_t)chunksize,ft->fp) 
					!= chunksize)
				fail("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 = rlong(ft);
			if (chunksize & 1)
				chunksize++;
			chunk_buf = (char *) malloc(chunksize + 1);
			if (fread (chunk_buf,1,(size_t)chunksize,ft->fp) 
					!= chunksize)
				fail("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 = rlong(ft);
			if (chunksize != 4) 
				fail("8SVX: Short channel chunk");
			channels = rlong(ft);
			channels = (channels & 0x01) + 
					((channels & 0x02) >> 1) +
				   	((channels & 0x04) >> 2) + 
					((channels & 0x08) >> 3);

			continue;
		}

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

	}

	if (rate == 0)
		fail ("8SVX: invalid rate");
	if (strncmp(buf,"BODY",4) != 0)
		fail ("8SVX: BODY chunk not found");
	p->nsamples = rlong(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(ft->filename, READBINARY)) == NULL)
			fail("Can't open channel file '%s': %s",
				ft->filename, strerror(errno));

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

/*======================================================================*/
/*                         8SVXREAD                                     */
/*======================================================================*/
LONG svxread(ft, buf, nsamp) 
ft_t ft;
LONG *buf, nsamp;
{
	ULONG 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                                 */
/*======================================================================*/
void 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                               */
/*======================================================================*/
void svxstartwrite(ft)
ft_t ft;
{
	struct svxpriv *p = (struct svxpriv *) ft->priv;
	int i;

	int littlendian = 1;
	char *endptr;

	endptr = (char *) &littlendian;
	/* 8svx is in big endian format.  Swaps wahst
	 * read in on little endian machines.
	 */
	if (*endptr)
	{
		ft->swap = ft->swap ? 0 : 1;
	}

	/* open channel output files */
	p->ch[0] = ft->fp;
	for (i = 1; i < ft->info.channels; i++) {
		if ((p->ch[i] = tmpfile()) == NULL)
			fail("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);
}

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

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

	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                                */
/*======================================================================*/

void 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))
			fail ("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]);
	}

	/* add a pad byte if BODY size is odd */
	if(p->nsamples % 2 != 0)
		fputc('\0', ft->fp);

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

/*======================================================================*/
/*                         8SVXWRITEHEADER                              */
/*======================================================================*/
#define SVXHEADERSIZE 100
void svxwriteheader(ft,nsamples)
ft_t ft;
LONG nsamples;
{
	LONG formsize =  nsamples + SVXHEADERSIZE - 8;

	/* FORM size must be even */
	if(formsize % 2 != 0) formsize++;

	fputs ("FORM", ft->fp);
	wlong(ft, formsize);  /* size of file */
	fputs("8SVX", ft->fp); /* File type */

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

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

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

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