/*
 * August 21, 1998
 * Copyright 1998 Fabrice Bellard.
 *
 * [Rewrote completly the code of Lance Norskog And Sundry
 * Contributors with a more efficient algorithm.]
 *
 * 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.  
 */

/*

 * Modified for use in Speak Freely by John Walker: 2003-01-25

 * Sound Tools rate change effect file.
 */

#include "netfone.h"

/*
 * Linear Interpolation.
 *
 * The use of fractional increment allows us to use no buffer. It
 * avoid the problems at the end of the buffer we had with the old
 * method which stored a possibly big buffer of size
 * lcm(in_rate,out_rate).
 *
 * Limited to 16 bit samples and sampling frequency <= 65535 Hz. If
 * the input & output frequencies are equal, a delay of one sample is
 * introduced.	Limited to processing 32-bit count worth of samples.
 *
 * 1 << FRAC_BITS evaluating to zero in several places.  Changed with
 * an (unsigned long) cast to make it safe.  MarkMLl 2/1/99
 */

#define FRAC_BITS 16

/*
 * Prepare processing.
 */
void rate_start(rate_t *rate, int inrate, int outrate)
{
	unsigned long incr;

	rate->opos_frac=0;
	rate->opos=0;

	/* Increment */
	incr = (unsigned long)(((double) inrate) / ((double) outrate) * 
			   (double) (((unsigned long) 1) << FRAC_BITS));

	rate->opos_inc_frac = incr & ((((unsigned long) 1) << FRAC_BITS) - 1);
	rate->opos_inc = incr >> FRAC_BITS;

	rate->ipos = 0;
	rate->ilast = 0;
}

/*
 * Processed signed long samples from ibuf to obuf.
 * Return number of samples processed.
 */
void rate_flow(rate_t *rate, unsigned char *ibuf, unsigned char *obuf, 
			   int *isamp, int *osamp)
{
	unsigned char *istart, *iend;
	unsigned char *ostart, *oend;
	long ilast, icur, out;
	unsigned long tmp;
	double t;

	ilast = rate->ilast;

	istart = ibuf;
	iend = ibuf + *isamp;

	ostart = obuf;
	oend = obuf + *osamp;

	while (obuf < oend) {

		/* Safety catch to make sure we have input samples.  */
		if (ibuf >= iend) goto the_end;

		/* Read as many input samples so that ipos > opos */

		while (rate->ipos <= rate->opos) {
			ilast = audio_u2s(*ibuf++);
			rate->ipos++;
			/* See if we finished the input buffer yet */
			if (ibuf >= iend) goto the_end;
		}

		icur = audio_u2s(*ibuf);

		/* Interpolate */
		t = ((double) rate->opos_frac) / (((unsigned long) 1) << FRAC_BITS);
		out = (long) ((((double) ilast) * (1.0 - t)) + (((double) icur) * t));

		/* Output sample & increment position */

		*obuf++ = audio_s2u(out);

		tmp = rate->opos_frac + rate->opos_inc_frac;
		rate->opos = rate->opos + rate->opos_inc + (tmp >> FRAC_BITS);
		rate->opos_frac = tmp & ((((unsigned long) 1) << FRAC_BITS) - 1);
	}

the_end:
	*isamp = ibuf - istart;
	*osamp = obuf - ostart;
	rate->ilast = ilast;
}
