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

/*
** 	Echo effect. based on:
**
** echoplex.c - echo generator
**
** Copyright (C) 1989 by Jef Poskanzer.
**
** 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.  This software is provided "as is" without express or
** implied warranty.
*/

/*
 * Sound Tools echo effect file.
 */

#ifdef	AMIGA
#include <stdlib.h> /* Harmless, and prototypes atof() etc. --dgc */
#endif
#include <math.h>
#include "st.h"

#define FADE_THRESH 10
#define MYBUFSIZ 256
#define DELAYBUFSIZ ( 50L * MAXRATE )
#define MAXDELAYS 30

struct echoplex {
	int	counter;			
	int	numdelays;
	long	*delaybuf;
	float	delay[MAXDELAYS], atten[MAXDELAYS];
	long	samples[MAXDELAYS], maxsamples;
	long	pl, ppl, pppl;
};

/* Private data for SKEL file */
typedef struct echostuff {
	struct	echoplex *echoplex;
} *echo_t;

#ifndef abs
#define abs(a) ((a) >= 0 ? (a) : -(a))
#endif

long clip24();

IMPORT writing;

/*
 * Process options
 */
echo_getopts(effp, n, argv) 
eff_t effp;
int n;
char **argv;
{
	echo_t echo = (echo_t) effp->priv;
	struct echoplex *e;
	int i;

	echo->echoplex = (struct echoplex *) malloc(sizeof(struct echoplex));
	e = echo->echoplex;
	e->numdelays = 0;
	e->maxsamples = 0;

	if ((n == 0) || (n & 1))
	    fail("Usage: echo delay attenuation [ delay attenuation ... ]");

	i = 0;
	while (i < n) {
		/* Linux bug and it's cleaner. */
		sscanf(argv[i++], "%f", &e->delay[e->numdelays]);
		sscanf(argv[i++], "%f", &e->atten[e->numdelays]);

		e->numdelays++;
	}
}

/*
 * Prepare for processing.
 */
echo_start(effp)
eff_t effp;
{
	echo_t echo = (echo_t) effp->priv;
	struct	echoplex *e = echo->echoplex;
	int i;

	for(i = 0; i < e->numdelays; i++) {
		e->samples[i] = e->delay[i] * effp->ininfo.rate;
		if ( e->samples[i] < 1 )
		    fail(" delay must positive, aye!");
		if ( e->samples[i] > DELAYBUFSIZ )
			fail("Echo: delay must be less than %g seconds",
				DELAYBUFSIZ / (float) effp->ininfo.rate );
		if ( e->atten[i] < 0.0 )
		    fail("attenuation must positive, aye!\n" );
		if ( e->samples[i] > e->maxsamples )
		    e->maxsamples = e->samples[i];
	}
	if (! (e->delaybuf = (long *) malloc(sizeof (long) * e->maxsamples)))
		fail("Echo: Cannot malloc %d bytes\n", 
			sizeof(long) * e->maxsamples);
	for ( i = 0; i < e->maxsamples; ++i )
		e->delaybuf[i] = 0;
	e->pppl = e->ppl = e->pl = 0x7fffff;		/* fade-outs */
	e->counter = 0;
}

/*
 * Processed signed long samples from ibuf to obuf.
 * Return number of samples processed.
 */

echo_flow(effp, ibuf, obuf, isamp, osamp)
eff_t effp;
long *ibuf, *obuf;
int *isamp, *osamp;
{
	echo_t echo = (echo_t) effp->priv;
	struct	echoplex *e = echo->echoplex;
	int len, done;
	int i, j;
	
	long l;

	i = e->counter;
	len = ((*isamp > *osamp) ? *osamp : *isamp);
	for(done = 0; done < len; done++) {
		/* Store delays as 24-bit signed longs */
		l = *ibuf++ / 256;
		for ( j = 0; j < e->numdelays; ++j )
			l = l + 
e->delaybuf[( i + e->maxsamples - e->samples[j]) % e->maxsamples] * e->atten[j];
		l = clip24(l);
		e->delaybuf[i] = l;
		*obuf++ = l * 256;
		i++;		/* XXX need a % maxsamples here ? */
		i %= e->maxsamples;
	}
	e->counter = i;
	/* processed all samples */
}

/*
 * Drain out echo lines. 
 */
echo_drain(effp, obuf, osamp)
eff_t effp;
long *obuf;
long *osamp;
{
	echo_t echo = (echo_t) effp->priv;
	struct	echoplex *e = echo->echoplex;
	long l;
	int i, j, done;

	i = e->counter;
	done = 0;
	/* drain out delay samples */
	do {
		l = 0;
		for ( j = 0; j < e->numdelays; ++j )
			l += 
e->delaybuf[(i + e->maxsamples - e->samples[j]) % e->maxsamples] * e->atten[j];
		l = clip24(l);
		e->delaybuf[i] = l;
		obuf[done++] = l * 256;
		e->pppl = e->ppl;
		e->ppl = e->pl;
		e->pl = l;
		i++;		/* need a % maxsamples here ? */
		i %= e->maxsamples;
	} while((done < *osamp) && 
		((abs(e->pl) + abs(e->ppl) + abs(e->pppl)) > FADE_THRESH));
	e->counter = i;
	*osamp = done;
	/* drain will not be called again */
}

/*
 * Clean up echo effect.
 */
echo_stop(effp)
eff_t effp;
{
	echo_t echo = (echo_t) effp->priv;

	free((char *) echo->echoplex);
	echo->echoplex = (struct echoplex *) -1;   /* guaranteed core dump */
}

long
clip24(l)
long l;
{
	if (l >= ((long)1 << 24))
		return ((long)1 << 24) - 1;
	else if (l <= -((long)1 << 24))
		return -((long)1 << 24) + 1;
	else
		return l;
}


