/*
 * July 5, 1991
 * Copyright 1991 Lance Norskog And Sundry Contributors
 * This source code is freely redistributable and may be used for
 * any purpose.  This copyright notice must be maintained. 
 * Lance Norskog And Sundry Contributors are not responsible for 
 * the consequences of using this software.
 */

/*
 * Sound Tools skeleton file format driver.
 */

#include <linux/soundcard.h>
#include <sys/ultrasound.h>
#include "st.h"

struct pat_header {
    char            magic[12];
    char            version[10];
    char            description[60];
    unsigned char   instruments;
    char            voices;
    char            channels;
    unsigned short  nr_waveforms;
    unsigned short  master_volume;
    unsigned long   data_size;
};

struct sample_header {
    char            name[7];
    unsigned char   fractions;
    long            len;
    long            loop_start;
    long            loop_end;
    unsigned short  base_freq;
    long            low_note;
    long            high_note;
    long            base_note;
    short           detune;
    unsigned char   panning;

    unsigned char   envelope_rate[6];
    unsigned char   envelope_offset[6];

    unsigned char   tremolo_sweep;
    unsigned char   tremolo_rate;
    unsigned char   tremolo_depth;

    unsigned char   vibrato_sweep;
    unsigned char   vibrato_rate;
    unsigned char   vibrato_depth;

    char            modes;

    short           scale_frequency;
    unsigned short  scale_factor;
};

/* Private data for SKEL file */
typedef struct patstuff {
	long rest;
	struct pat_header header;
	struct sample_header sample;
} *pat_t;

IMPORT float volume, amplitude;
IMPORT int summary, verbose;

static ft_t pat_in_ft = NULL;

/*
 * Do anything required before you start reading samples.
 * Read file header. 
 *	Find out sampling rate, 
 *	size and style of samples, 
 *	mono/stereo/quad.
 */
patstartread(ft) 
ft_t ft;
{
	pat_t pat = (pat_t) ft->priv;
	char buf[256];
	int	littlendian = 1;
	char	*endptr;

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

	pat_in_ft = ft;

	/* If you need to seek around the input file. */
	if (! ft->seekable)
		fail(".pat input file must be a file, not a pipe");

	fread(buf, 0xef, 1, ft->fp);
	memcpy ((char *) &(pat->header), buf, sizeof (pat->header));

	if (strncmp (pat->header.magic, "GF1PATCH110", 12))
		fail("Sorry, not a PAT file");
	if (strncmp (pat->header.version, "ID#000002", 10))
		fail("PAT file is incompatible version");

	pat->header.nr_waveforms = *(unsigned short *) &buf[85];
	pat->header.master_volume = *(unsigned short *) &buf[87];

	/* first sample only? */
	fread(buf, sizeof(pat->sample), 1, ft->fp);
	memcpy ((char *) &(pat->sample), buf, sizeof (pat->sample));

	/*
	 * Since some fields of the patch record are not 32bit aligned, we must
	 * handle them specially.
	 */
	pat->sample.low_note = *(long *) &buf[22];
	pat->sample.high_note = *(long *) &buf[26];
	pat->sample.base_note = *(long *) &buf[30];
	pat->sample.detune = *(short *) &buf[34];
	pat->sample.panning = (unsigned char) buf[36];

	memcpy (pat->sample.envelope_rate, &buf[37], 6);
	memcpy (pat->sample.envelope_offset, &buf[43], 6);

	pat->sample.tremolo_sweep = (unsigned char) buf[49];
	pat->sample.tremolo_rate = (unsigned char) buf[50];
	pat->sample.tremolo_depth = (unsigned char) buf[51];

	pat->sample.vibrato_sweep = (unsigned char) buf[52];
	pat->sample.vibrato_rate = (unsigned char) buf[53];
	pat->sample.vibrato_depth = (unsigned char) buf[54];
	pat->sample.modes = (unsigned char) buf[55];
	pat->sample.scale_frequency = *(short *) &buf[56];
	pat->sample.scale_factor = *(unsigned short *) &buf[58];

	pat->rest = pat->sample.len;

	if (pat->sample.modes & WAVE_16_BITS) ft->info.size = WORD;
	else ft->info.size = BYTE;
	if (pat->sample.modes & WAVE_UNSIGNED) ft->info.style = UNSIGNED;
	else ft->info.style = SIGN2;

	/*
	 * If your format specifies or your file header contains
	 * any of the following information. 
	 */
	ft->info.rate = pat->sample.base_freq;
	ft->info.channels = 1;
	ft->comment = NULL;
}

/*
 * Read up to len samples from file.
 * Convert to signed longs.
 * Place in buf[].
 * Return number of samples read.
 */

patread(ft, buf, len) 
ft_t ft;
long *buf, len;
{
	pat_t pat = (pat_t) ft->priv;
	int offset, nsamp, fsamp;
	int abs;
	float amp;
	int done = 0;
	
	char c;
	unsigned char uc;
	short s;
	unsigned short us;
	long l;
	unsigned long ul;
	float f;
	double d;

	if (pat->rest <= 0) return(0);

	/* past pat header and first sample header */
	offset = 0xef + 96;
	/* past what was already read */
	offset += pat->sample.len - pat->rest;

	fseek(ft->fp, offset, 0);

	nsamp = pat->rest;
	if (ft->info.size == WORD) nsamp /= 2;

	if (len > nsamp) len = nsamp;
	if (len == 0) return 0;
	done = rawread(ft, buf, len);
	if (done == 0) report("Premature EOF on .pat input file");
	pat->rest -= done;
	if (ft->info.size == WORD) pat->rest -= done;
	return done;
}

/*
 * Do anything required when you stop reading samples.  
 * Don't close input file! 
 */
patstopread(ft) 
ft_t ft;
{
}


static void
patfillhdr(ft) 
ft_t ft;
{
	pat_t	pat = (pat_t) ft->priv, pat_in;

	if (pat_in_ft != NULL && !strcmp(pat_in_ft->filetype, "pat")) {
		pat_in = (pat_t) pat_in_ft->priv;
		memcpy ((char *) &(pat->header),
			(char *) &(pat_in->header), sizeof (pat->header));
		memcpy ((char *) &(pat->sample),
			(char *) &(pat_in->sample), sizeof (pat->sample));
		return;
	}

	memset ((char *) &(pat->header), 0, sizeof (pat->header));
	memcpy (pat->header.magic, "GF1PATCH110", 12);
	memcpy (pat->header.version, "ID#000002", 10);
	pat->header.nr_waveforms = 1;
	pat->header.master_volume = 127;

	memset ((char *) &(pat->sample), 0, sizeof (pat->sample));
	if (ft->info.size == WORD) pat->sample.modes |= WAVE_16_BITS;
	else ft->info.size = BYTE;
	if (ft->info.style == UNSIGNED) pat->sample.modes |= WAVE_UNSIGNED;
	else ft->info.style = SIGN2;
}

static void
patwritehdr(ft) 
ft_t ft;
{
	pat_t	pat = (pat_t) ft->priv;
	char buf[256];

	if (strncmp (pat->header.magic, "GF1PATCH110", 12))
		patfillhdr(ft);

	memset (buf, 0, 256);
	memcpy (buf, (char *) &(pat->header), sizeof (pat->header));
	*(unsigned short *) &buf[85] = pat->header.nr_waveforms;
	*(unsigned short *) &buf[87] = pat->header.master_volume;

	if (fwrite(buf, 1, 0xef, ft->fp) != 0xef)
		fail("can't write .pat header");

	memset (buf, 0, 256);
	memcpy (buf, (char *) &(pat->sample), sizeof (pat->sample));
	/*
	 * Since some fields of the patch record are not 32bit aligned, we must
	 * handle them specially.
	 */
	*(long *) &buf[22] = pat->sample.low_note;
	*(long *) &buf[26] = pat->sample.high_note;
	*(long *) &buf[30] = pat->sample.base_note;
	*(short *) &buf[34] = pat->sample.detune;
	(unsigned char) buf[36] = pat->sample.panning;

	memcpy (&buf[37], pat->sample.envelope_rate, 6);
	memcpy (&buf[43], pat->sample.envelope_offset, 6);

	(unsigned char) buf[49] = pat->sample.tremolo_sweep;
	(unsigned char) buf[50] = pat->sample.tremolo_rate;
	(unsigned char) buf[51] = pat->sample.tremolo_depth;

	(unsigned char) buf[52] = pat->sample.vibrato_sweep;
	(unsigned char) buf[53] = pat->sample.vibrato_rate;
	(unsigned char) buf[54] = pat->sample.vibrato_depth;
	(unsigned char) buf[55] = pat->sample.modes;
	*(short *) &buf[56] = pat->sample.scale_frequency;
	*(unsigned short *) &buf[58] = pat->sample.scale_factor;

	if (fwrite(buf, 1, 96, ft->fp) != 96)
		fail("can't write .pat header");
}

patstartwrite(ft) 
ft_t ft;
{
	pat_t pat = (pat_t) ft->priv;
	int	littlendian = 1;
	char	*endptr;

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

	/* If you have to seek around the output file */
	if (! ft->seekable)
		fail("Output .pat file must be a file, not a pipe");

	pat->rest = 0;
	patwritehdr(ft);
	ft->comment = "gus format";
}


patwrite(ft, buf, len) 
ft_t ft;
long *buf, len;
{
	pat_t pat = (pat_t) ft->priv;

	pat->rest += len;
	rawwrite(ft, buf, len);
}

patstopwrite(ft) 
ft_t ft;
{
	pat_t pat = (pat_t) ft->priv;
	int nsamp;

	/* All samples are already written out. */
	/* If file header needs fixing up, for example it needs the */
 	/* the number of samples in a field, seek back and write them here. */
	if (!ft->seekable)
		return;
	if (fseek(ft->fp, 0L, 0) != 0)
		fail("Sorry, can't rewind output file to rewrite .pat header.");
	pat->header.nr_waveforms = 1;
	nsamp = pat->rest;
	if (ft->info.size == WORD) nsamp *= 2;
	pat->sample.len = nsamp;
	pat->sample.loop_end = nsamp;
	patwritehdr(ft);
}

