
/*
 * 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 prediction-correction compression effect file.
 * Experiment with various simple equation systems.
 * 
 * This is not ready for prime time.  It's here for research purposes.
 * Sox will hang if you run this as is.  Define D0 or D1, recompile,
 * and try compressing the output with 'compress' and 'pack'.  
 *
 * Inspired by 2D PC gem in Graphics Gems II.
 */

#include "st.h"

#define	D0	/* output difference between successive samples */
/* #define	D1	/* guess by extending slope of last two samples */
/* #define	D2 	/* extend second derivate and guess signal turn */
/* Autocorrelation isn't worth pursuing.  D2 should do an excellent job */

/* Private data for Prediction-Correction state machine */
typedef struct predstuff {
	int	direction;		/* 0 for compress, 1 for decompress */
	int	first;			/* first time through? */
	u_i	error;			/* average error output */
	int	clipped;		/* # of clipped error values */
#ifdef	D0 
	long	in[1];			/* previous input sample */
#endif
#ifdef	D1
	long	in[2];			/* previous input samples */
#endif
} *pred_t;

long pred_init(), pred_ict(), pred_next();

/*
 * Process options
 */
pred_getopts(effp, n, argv) 
eff_t effp;
int n;
char **argv;
{
	pred_t pred = (pred_t) effp->priv;

	if ((n != 1) || 
		    (strcmp(argv[0], "-c") && strcmp(argv[0], "-d")))
		fail("Linp compression requires in or out options.");

	pred->direction = strcmp(argv[0], "-c");
}

/*
 * Start processing
 */
pred_start(effp)
eff_t effp;
{
	pred_t pred = (pred_t) effp->priv;

	pred->error = 0;
	pred->first = 1;
	pred->clipped = 0;
}

/*
 * Process according to compression direction.
 * Both loops use the same state machine, 
 * but feed it from different streams.
 */
/*
 * If first time, emit first two samples.
 * Then,
 */

pred_flow(effp, ibuf, obuf, isamp, osamp)
eff_t effp;
long *ibuf, *obuf;
int *isamp, *osamp;
{
	int len, done;
	pred_t pred = (pred_t) effp->priv;
	register long predict, error;
	
	done = 0;
	if (pred->first) {
		done = pred_init(effp, ibuf, obuf);
		ibuf += done;
		obuf += done;
		pred->first = 0;
	}
	len = ((*isamp > *osamp) ? *osamp : *isamp);
	if (done > len)		/* it can't happen here */
		fail("Prediction effect: not enough samples?");
	if (pred->direction) {			/* decompress */
		for(; done < len; done++) {
			/* reconstitute sample from prediction and error */
			predict = pred_ict(effp);
			error = *ibuf;
			pred_next(effp, predict + error);
			pred->error = pred->error/2 + abs(error)/2;
			*obuf++ = predict + error;
			ibuf++;
		}
	} else {				/* compress */
		for(; done < len; done++) {
			/* generate sample from prediction and error */
			predict = pred_ict(effp);
			error = *ibuf - predict;
			pred->error = pred->error/2 + abs(error)/2;
			if (predict + error != *ibuf)
				pred->clipped++;
			pred_next(effp, *ibuf);
			ibuf++;
			*obuf++ = error;
		}
	}
}

/* 
 * Linear Prediction state machine part A.
 * 
 * Initialize from buffer.  Return number of samples processed.
 * It will be the same for input and output streams.
 */
long
pred_init(effp, ibuf, obuf)
eff_t effp;
long *ibuf, *obuf;
{
	pred_t pred = (pred_t) effp->priv;

	/* 
	 * This is bogus!  
	 * Just pretend samples in negative time are 0, make a first few
	 * weird guesses.
	 */
#ifdef	D0
	/* same for compress and decompress */
	pred->in[0] = *obuf++ = *ibuf++;
	return 1;
#endif
#ifdef	D1
	/* same for compress and decompress */
	pred->in[0] = *obuf++ = *ibuf++;
	pred->in[1] = *obuf++ = *ibuf++;
	return 2;
#endif
}

/* 
 * Linear Prediction state machine part B.
 * 
 * Emit a predicted sample.
 */
long
pred_ict(effp)
eff_t effp;
{
	pred_t pred = (pred_t) effp->priv;

#ifdef	D1
	avg = (pred->in[0]/2 + pred->in[1]/2);
	return pred->in[1] + (pred->in[1] - avg);
#endif
#ifdef	D0
	/* Assume flat data */
	return pred->in[0];
#endif
}

/* 
 * Linear Prediction state machine, part C.
 * 
 * Process next sample.
 */
long
pred_next(effp, samp)
eff_t effp;
long samp;
{
	pred_t pred = (pred_t) effp->priv;

#ifdef	D1
	pred->in[0] = pred->in[1];
	pred->in[1] = samp;
#endif
#ifdef	D0
	/* Assume flat data */
	pred->in[0] = samp;
#endif
}

/*
 * Do anything required when you stop reading samples.  
 * Don't close input file! 
 */
pred_stop(effp)
eff_t effp;
{
	pred_t pred = (pred_t) effp->priv;
	int error;
	int size;

	/* XXX Or should it always be the input size? */
	if (pred->direction)
		size = effp->ininfo.size;
	else
		size = effp->outinfo.size;
	switch(size) {
		case WORD:
			error = pred->error / ((long)1 << 16);
			break;
		case BYTE:
			error = pred->error / ((long)1 << 24);
			break;
		default:
			error = pred->error;
			break;
	}
	/* nothing to do */
	fprintf(stderr, "Prediction\n\tAverage Error outputs: %d\n", error);
	fprintf(stderr, "\tClipped error outputs: %d\n", pred->clipped);
}





