/*	UGENS2.C Modified by Robin Whittle 28 August 1995 . . . 
 *
 * !    Bug fixed 1 February 1996:
 * !
 * !	The offset was not being used.  Identical bugs fixed in:
 * !	ftkrch() and ftkrchk()
 *
 *   	
 *
 * NOTE - this code will only operate with a new version of fgens.c (for
 *	a perf time table finder), a modified prototyp.h (which declares it)
 *	and with a changed version of entry.c.
 *	See notes below.
 *
 * Alterations to table and tablei code to:
 *-----------------------------------------
 *
 * 1 - 	Alter non-wrap behavior at the end of the table in non-interpolated
 * 	read.  
 *
 * 2 - 	Alter non-wrap mode below 0 and above table length in interpolated 
 * 	read.
 *
 *	Non-wrap is now known as limit mode.
 * 	The purpose of these is to make the output of the table read "stick" 
 *	or "limit" to the values read for the lowest (0) and highest index 
 *	values, when (in non-wrap mode) the final index exceeds the limit 
 *	of the table.
 *
 * 3 - 	Fix a bug which produced idiosyncratic (and almost certainly useless)
 *	results with interpolated table read when the ixoff pointed 
 *	to somewhere between the integer index values which point to the 
 *	elements in the table.  This happened in wrap and non-wrap modes.
 *
 *	tablei p4, p5, 0, 2	OK, the offset points to location 2.
 *	tablei p4, p5, 0, 2.3	Bad.
 *	tablei p4, p5, 1, 0.5	OK, the offset points to the middle location.
 *	tablei p4, p5, 1, 0.501	Bad, the offset points just beyond the middle.
 *
 * 4 -	Fixed a problem in non-interpolated, wrap mode for negative final 
 *	indexes - the -1 to -0.0001 range was being converted to address 0
 *	when it should have been converted to -1.  In wrap mode, this meant
 * 	The entire negative index response was addressing one too high in 
 *	the table.
 *
 *	Changes have been made to the following functions:
 *
 *	tblchk()
 *	ktable()
 *  	table()
 *	ktablei()
 *	tablei()
 *
 *	Because of the number of changes, they have not been marked.
 *
 * 5 -	Provided for the possiblity of a k rate variable controlling the 
 *	table number.  Previously only an i rate variable could be used.
 *
 *	This is achieved with two new ugens:
 *
 *	tablekt and tableikt
 *
 * 	See "opodlst etc." below for changes required in entry.c.
 *
 * 6 -	ugens2.h has been modified to comment the data structures.  It is 
 * 	much the same as before, except I have changed the name of *indx to 
 *	*xndx and *ifn to *xfn and to add a new variable pfn for the previous
 *	function table number.
 *
 * This file written with tabstops every 8 columns.
 */		


/* opcodlst etc. - notes on changes to be made to entry.c
 * =============   --------------------------------------
 *
 * The opcoslst in entry.c is the central control for parsing ugens and 
 * deciding which functions to call.
 *
 * Previously the table and tablei ugens had the following entries:
 *
 * opcode   length of 	thread	output	input	functions to call at:
 *	    data space		args	args	i time	k rate	a rate

{ "itable", S(TABLE),	1,	"i",	"iiooo",itable	 		},
{ "itablei", S(TABLE),	1,	"i",	"iiooo",itabli	 		},

{ "table",  S(TABLE),	7,	"s",	"xiooo",tblset,	ktable,	table	},
{ "tablei", S(TABLE),	7,	"s",	"xiooo",tblset,	ktabli,	tabli	},

 * Although the ugen name is "table" or "tablei", special code in rdorch.c 
 * looks for these opcodes, and can decide to prepend i to them if certain
 * conditions are met.  The code will then find "itable" or "itablei" in 
 * the opcodlst, and work from there.
 *
 * These four lines remain.  However we need different code to handle 
 * table numbers which could change at k rate. This means we need
 * new lines in opcodlst to match the new ugen names with the new functions:

{ "tablekt",  S(TABLE),	7,	"s",  "xkooo",tblsetkt,	ktablekt, tablekt },
{ "tableikt", S(TABLE),	7,	"s",  "xkooo",tblsetkt,	ktablikt, tablikt },

 * Also we need to add function prototypes for these five new functions to
 * the first section of entry.c:

void tblsetkt(void*), ktablekt(void*), tablekt(void*), ktablikt(void*), tablikt(void*);

 */


/* Discussion on coping with various combinations of parameter type
 * ----------------------------------------------------------------
 *
 * Since there are three types of variable (i, k and a) and since the 
 * table read operation may well depend on the types of variable for output
 * and two input variables, there are 27 possiblilities - many of which we
 * want to disallow.  There really needs to be a better way of specifying
 * in opcodlst which sorts of parameters we expect, and what the thread and
 * functions will be.  This can be done with multiple names for similar
 * unit generators - but this is messy.  It is the technique usually adopted 
 * - for intance ilinrand, klinrand and alinrand.
 *
 * A better approach would be for certain opcodes (listed specifically on some
 * list) to have their names expanded according to the argument types on the
 * line in which they are called.  Then a search of opcodlst would proceed.
 *
 * For instance "kblah table kzot, 3" would cause "table" to be expanded to 
 * "table_k/kc" for types k, k and constant.  An appropriate line in opcodlist
 * could have this and all would be well.  There are probably better approaches
 * than this.
 *
 * I do not want to attempt such basic changes to Csound, so to get the 
 * table read opcodes working with either k or i rate variables, I decided to 
 * creat two new ugen names.
 */

/*****************************************************************************/
 
#include "cs.h"			/*			UGENS2.C	*/
#include "ugens2.h"
#include <math.h>

static float	fzero = 0., fone = 1.;

void phsset(PHSOR *p)
{
register float	phs;
register long  longphs;
	if ((phs = *p->iphs) >= fzero) {
		if ((longphs = phs))
			warning("init phase truncation");
		p->curphs = phs - longphs;
	}
}

void kphsor(PHSOR *p)
{
register float	phs;
	*p->sr = phs = p->curphs;
	if ((phs += *p->xcps * onedkr) >= fone)
		phs -= fone;
	else if (phs < fzero)
		phs += fone;
	p->curphs = phs;
}

void phsor(PHSOR *p)
{
register int	nsmps = ksmps;
register float	*rs, phase, incr;

	rs = p->sr;
	phase = p->curphs;
	if (p->XINCODE) {
		register float *cps = p->xcps;
		do {
			incr = *cps++ / esr;
			*rs++ = phase;
			phase += incr;
			if (phase >= fone)
				phase -= fone;
			else if (phase < fzero)
				phase += fone;
		} while (--nsmps);
	}
	else {
		incr = *p->xcps / esr;
		do {
			*rs++ = phase;
			phase += incr;
			if (phase >= fone)
				phase -= fone;
			else if (phase < fzero)
				phase += fone;
		} while (--nsmps);
	}
	p->curphs = phase;
}

/*****************************************************************************/
/*****************************************************************************/

					/* Table read code - see TABLE 
					 * data structure in ugens2.h.
					 */


					/*************************************/

					/* itblchk() 
					 *
				 	 * This is called at init time
					 * by tblset() to set up the TABLE
					 * data structure for subsequent
					 * k and a rate operations.
					 *
					 * It is also called at init time by
					 * itable() and itablei() prior to 
					 * them calling ktable() and ktabli()
					 * respectively to produce a single
					 * result at init time.
					 *
					 * Here we need to find the table and
					 * set up variables in TABLE.  We need
					 * to generate errors in a way suitable
					 * for init time.
					 *
					 * A similar function - ptblchk() 
					 * does the same job, but reports 
					 * errors in a way suitable for 
					 * performance time.
					 */

					/* If the specified table number can
					 * be found, then the purpose is to 
					 * read the three i rate input 
					 * variables and the function table 
					 * number input variable - (which
					 * we can assume here is also i rate)
					 * to set up the TABLE data structure 
					 * ready for the k and a rate 
					 * functions.
					 */
int itblchk(TABLE *p)
{
					/* No need to check for a 0 or negative
					 * table number - ftfind will do that.
					 */

					/* Get pointer to the function table
					 * data structure of the table number
					 * specified in xfn. Return 0 if
					 * it cannot be found.
					 *
					 * ftfind() generates an error message 
					 * if the table cannot be found. This
				 	 * works OK at init time.
					 */

	if ((p->ftp = ftfind(p->xfn)) == NULL)
		return(NULL);

					/* Table number is valid.
					 *
					 * Although TABLE has an integer
					 * variable for the table number
					 * (p->pfn) we do not need to 
					 * write it.  We know that
					 * the k and a rate functions which
					 * will follow will not be expecting
					 * a changed table number.
					 *
					 * p->pfn exists only for checking
					 * table number changes for functions
					 * which are expecting a k rate
					 * table number.
					 */

					/* Set denormalisation factor to 1 or 
					 * table length, depending on the 
					 * state of ixmode.
					 * 1L means a 32 bit 1.
					 */
	if (*p->ixmode)
		p->xbmul = p->ftp->flen;

	else	p->xbmul = 1L;

					/* Multiply the ixoff value by the 
					 * xbmul denormalisation factor and 
					 * then check it is between 0 and the
					 * table length.
					 *
  					 * Bug fix: was p->offset * *p->ixoff
					 */

	if (    ( p->offset = p->xbmul * *p->ixoff) < 0. 
	     || p->offset > p->ftp->flen
           )
	{
		sprintf(errmsg, "Offset %f < 0 or > tablelength", p->offset);
		initerror(errmsg);
		return(NULL);
	}

	p->wrap   = *p->iwrap;

					/* Return 1 to say everything is OK
					 */	
	return(1);
}

					/*************************************/

					/* ptblchk() 
					 *
				 	 * This is called at init time
					 * by tblsetkt() to set up the TABLE
					 * data structure for subsequent
					 * k and a rate operations which are
					 * expecting the table number to 
					 * change at k rate.
					 *
					 * tblsetkt() does very little - 
					 * just setting up the wrap variable
					 * in TABLE. All the other variables
					 * depend on the table number. This
					 * is not available at init time,
					 * so the following 4 functions must
					 * look for the changed table number
					 * and set up the variables accordingly
					 * - generating error messages in a 
					 * way which works at performance time.
					 *
					 * k rate   a rate
					 *
					 * ktablekt tablekt   Non interpolated
					 * ktablikt tablikt   Interpolated
					 * 
					 */
ptblchk(TABLE *p)
{
					/* TABLE has an integer variable for 
					 * the previous table number (p->pfn).
					 *
					 * Now (at init time) we do not 
					 * know the function table number 
					 * which will be provided at perf
					 * time, so set p->pfn to 0, so that
					 * the k or a rate code will recognise
					 * that the first table number is
					 * different from the "previous" one.
					 */
	p->pfn = 0;

					/* The only other thing to do is
					 * write the wrap value into the 
					 * immediate copy of it in TABLE.
					 */
	p->wrap   = *p->iwrap;
}



/*---------------------------------------------------------------------------*/
				
					/* tblset() 
					 *
					 * This is called at init time 
					 * to set up TABLE for the a and k
					 * rate table read functions which
					 * are expecting the table number to
					 * be fixed at i time. 
					 *
					 * Call the itblchk() function to 
					 * do the work.
					 */ 	
void tblset(TABLE *p)
{
	itblchk(p);
}
					
					/* tblsetkt() 
					 *
					 * This is called at init time 
					 * to set up TABLE for the a and k
					 * rate table read functions which
					 * are expecting the table number to
					 * be a k rate variable. 
					 *
					 * Call the ptblchk() function to 
					 * do the work.
					 */ 	
void tblsetkt(TABLE *p)
{
	ptblchk(p);
}

					/*************************************/

					/* Special functions to use when
					 * the output value is an init time 
					 * variable.
					 *
					 * These are called by the opodlst
					 * lines for itable and itablei 
					 * ugens.
					 *
					 * They call itblchk() and if the 
					 * table was found, they call the
					 * k rate function just once.
					 *
					 * If the table was not found, an
					 * error will result from ftfind.
					 */
void itable(TABLE *p)
{
void ktable();

	if (itblchk(p))
		ktable(p);
}
 
void itabli(TABLE *p)
{
void ktabli();

	if (itblchk(p))
		ktabli(p);
}

/*---------------------------------------------------------------------------*/

					/* Functions which read the table.
					 *
					 * First we have the four basic
					 * functions for a and k rate, 
					 * non interpolated and interpolated
					 * reading.  These all assume that
					 * the TABLE data structure has been
					 * correctly set up - they are not
					 * expecting the table number to 
					 * change at k rate.
					 *
					 * These are:
					 *
					 * k rate  a rate
					 *
					 * ktable  table   Non interpolated
					 * ktabli  tabli   Interpolated
					 * 
					 *
					 * Then we have	four more functions
					 * which are expecting the table 
					 * number to change at k rate.
					 * They deal with this, and then
					 * call one of the above functions
					 * to do the reading.
					 *
					 * These are:
					 *
					 * k rate   a rate
					 *
					 * ktablekt tablekt   Non interpolated
					 * ktablikt tablikt   Interpolated
					 * 
					 */

					/* ktable() and ktabli()
					 * ---------------------
					 *
					 * These both read a single value from 
					 * the table. ktabli() does it with
					 * interpolation.
					 *
					 * This is typically used for k rate
					 * reading - where they are called as
					 * a result of being listed in a line
					 * in opcodlst.  They are also called
					 * by two functions which after they 
					 * have coped with any change in
					 * the k rate function table number.
					 *
					 * ktablekt() and ktablikt().
					 *
					 * In addition, they can be called 
					 * by the init time functions:
					 * itable() and itabli().	
					 *
					 *
					 * table() and tabli()
					 * -------------------
					 *
					 * These do the reading at a rate
					 * with an a rate index.
					 *
					 * They are called directly via their
					 * entries in opcodlst, and also by
					 * two functions which call them after
					 * they have coped with any change in
					 * the k rate function table number.
					 *
					 * tablekt() and tablikt().
					 *
					 *	
					 */ 

					/*************************************/

					/* ktable()
					 */

void ktable(TABLE  *p)
{
					/* Local variables . . .
					 *
					 * Pointer to function table data 
					 * structure.
					 */
FUNC 	*ftp;
					
					/* 32 bit integers for pointing into
					 * table and for the table length - 
					 * which is always a power of 2. 
					 * The table must actually be one 
					 * more longer than this if it has a
					 * guard point.
					 */
register long	indx, length;

					/* Float for calculating where we are
					 * going to read from.
					 */
register float 	ndx;
			
					/*-----------------------------------*/

					/* Assume that TABLE variables have
					 * been set so everything is ready to 
					 * go!
					 */
		
					/* Set up local variables.
					 */				
	ftp    = p->ftp;
	ndx    = *p->xndx;
	length = ftp->flen;
					/* Multiply ndx by denormalisation 
					 * factor.  and add in the offset -
					 * already denormalised - by tblchk().  
					 *
					 * xbmul = 1 or table length depending
					 * on state of ixmode.
					 */

	ndx = ( ndx * p->xbmul) + p->offset;		

					/* ndx now includes the offset and is
					 * ready to address the table.  
					 *
					 * The original code was:
					 *
					 *  indx = (long) (ndx + p->offset);
					 *
					 * This is a problem, because 
					 * converting a float to a long integer
					 * in this way, (at least with the 
					 * MSDOS GNU DJGPP compiler) causes
					 * problems with negative numbers.
					 *
					 * We want anything between -1.0 and 
					 * just below zero to be converted
					 * to -1. But they are converted to 0.
					 * Then -2.0 to -1.0001 is converted
					 * to -1, when we want -2.
					 *
					 * Negative indexes are an issue with
					 * wrap mode, so we have to convert
					 * the way we want.
					 * 
					 * ANSI C library function floor
				 	 * finds the next most negative integer
					 * - returning it as a float.
					 *
					 * There may be faster ways of doing
					 * it, but the results would be
					 * machine dependant. 
					 */
	indx = (long) floor(ndx);

					/* Now for "limit mode" - the "non-wrap" 
					 * function, depending on iwrap.
					 *
					 * The following section of code 
					 * limits the final index to 0 and
					 * the last location in the table.
					 * 
					 * It is only used when wrap is OFF. 
					 * The wrapping is acheived by code 
					 * after this - when this code is
					 * not run.
 					 */ 
	if (!p->wrap) {
					/* Previously this code limited the 
					 * upper range of the indx to the 
					 * table length - for instance 8.
					 * Then the result was ANDed with
					 * a mask (for instance 7).
					 *
					 * This meant that when the input 
					 * index was 8 or above, it got
					 * reduced to 0.  What we want is for
					 * it to stick at the index which reads
					 * the last value from the table - in
					 * this example from location 7.
					 *
					 * So instead of limiting to the 
					 * table length, we limit to 
					 * (table length - 1).
					 */
		if (indx > length - 1)
			indx = length - 1;

					/* Now limit negative values to zero.
					 * 0L is a macro for a long zero.  
					 * Why is this used?
					 */
		else if (indx < 0L)
			indx = 0L;
	}
					/* The following code uses an AND with
					 * an integer like 0000 0111 to 
					 * wrap the current index within the
					 * range of the table.
					 * In the original version, this
					 * code always ran, but with the new
					 * (length - 1) code above, it would
					 * have no effect, so it is now an else
					 * operation - running only when
					 * iwrap = 1.  This may save half a 
					 * usec or so. 
					 */
	else	indx &= ftp->lenmask;

					/* Now find the address of the start
					 * of the table, add it to the index,
					 * read the value from there and 
					 * write it to the destination.
					 */
	*p->rslt = *(ftp->ftable + indx);
}

					/*************************************/

						
					/* table()
					 */

					/* table() is similar to ktable() 
					 * above, except that it processes
					 * an array of input indexes, to 
					 * send results to another array.
					 * These arrays are ksmps long.
					 */
void table(TABLE  *p)
{
					/* Local variables . . .
					 *
					 * Pointer to function table data 
					 * structure.
					 */
register FUNC 	*ftp;

					/* Pointers to floats:
					 *
					 * *rslt	Array where the results
					 *		will be written.
					 *
					 * *pxndx	Array of input index
					 *		values.
					 *
					 * *tab		Pointer to start of 
					 * 		the table we will read.
					 */
register float 	*rslt, *pxndx, *tab;

					/* 32 bit integers:
					 *
					 * indx		Used to read table.
					 *		
					 * mask		ANDed with indx to 
					 * 		make it wrap within
					 *		table.
					 *
					 * length	Length of table - 
					 *		always a power of two, 
					 * 		even if the table has
					 *		a guard point.
					 *
					 * For instance length = 8, mask = 
					 * 0000 0111, normal locations in table
					 * are 0 to 7.  Location 8 is the 
					 * guard point.  table() does not read 
					 * the guard point - tabli() does.
					 */
register long	indx, mask, length;

					/* nsmps is the counter for the loop 
					 * which processes the data.
					 * declare it as an integer, and 
					 * intialize it to the global variable
					 * ksmps.
					 */
register int	nsmps = ksmps;

					/* Float local variables.
					 *
					 * ndx		Index.
					 *	
					 * xbmul	Normalisation factor
					 * 		to adjust for ndx 
					 * 		having a 0 to 1 range.
					 *		Equal to 1 or length
					 * 		depending on ixmode.
					 * 		
					 * offset	Offset to add to ndx.
					 *		Already denormalised.
					 */
float	ndx, xbmul, offset;

					/*-----------------------------------*/

					/* Assume that TABLE variables have
					 * been set so everything is ready to 
					 * go!
					 */
		
					/* Set up local variables.
					 */
	ftp    = p->ftp;
	rslt   = p->rslt;
	length = ftp->flen;
	pxndx  = p->xndx;
	xbmul  = p->xbmul;
	offset = p->offset;
	mask   = ftp->lenmask;
	tab    = ftp->ftable;
					/* Main loop - for the number of a
					 * samples in a k cycle.
					 */
	do {
					/* Read in the next raw index and 
					 * increment the pointer ready for
					 * the next cycle.
					 *
					 * Then multiply the ndx by the 
					 * denormalising factor and add in
					 * the offset.
					 */ 

		ndx = (*pxndx++ * xbmul) + offset;

					/* See notes in ktable() about this
					 * conversion to a 32 bit integer.
					 */
		indx = (long) floor(ndx);

					/* Limit = non-wrap.  Limits to 0 and
					 * (length - 1), or does the wrap 
					 * code.  See notes above in ktable().
					 */ 
		if (!p->wrap) {
			if (indx > length - 1)  
				indx = length - 1;
			else if (indx < 0L)  
				indx = 0L;
		}
					/* Now do the wrap code only if we are
					 * not doing the non-wrap code.
					 */			
		else	indx &= mask;
					/* Add add the index to the table 
					 * starting address, read the value
					 * and write it to the result array.
					 */
		*rslt++ = *(tab + indx);
	} 
	while(--nsmps);
}


					/*************************************/


					/* ktabli()
					 */
		
					/* ktabli() is similar to ktable() 
					 * above, except that it uses the
					 * fractional part of the final index 
					 * to interpolate between one value in
					 * the table and the next.
					 * 
					 * This means that it may read the
					 * guard point.  In a table of 
					 * "length" 8, the guardpoint is
					 * at locaton 8. The normal part of the
					 * table is locations 0 to 7.
					 *
					 * Previously this code did not cope
					 * properly with non wrap mode.
					 * Outside the 0 to 7.99 range (in
					 * the example of a table length 8),
					 * it repeated the outputs of the 
					 * 0 to 0.99 range of the table.
					 *
					 * Also there was a bug when the 
					 * offset did not point exactly to 
					 * an entry in the table.
					 *
					 * In non-wrap mode, when the final
					 * index is negative, the output
					 * should be the value in location 0.
					 * 
					 * In non-wrap mode, when the final 
					 * index is >= length, then the output 
					 * should be the value in the guard 
 					 * point location.
					 */
void ktabli(TABLE  *p)
{
					/* Local variables . . .
					 *
					 * Pointer to function table data 
					 * structure.
					 */
register FUNC 	*ftp;

					/* 32 bit integers for pointing into
					 * table and for the table length - 
					 * which is always a power of 2. 
					 * The table must actually be one 
					 * more longer than this if it has a
					 * guard point.
					 */
register long	indx, length;

					/* Variables for reading from two
					 * locations, and interpolating
					 * between them.
					 */
register float 	v1, v2, fract, ndx;

					/*-----------------------------------*/

					/* Assume that TABLE variables have
					 * been set so everything is ready to 
					 * go!
					 */
					
					/* Set up local variables.
					 */
	ftp    = p->ftp;
	ndx    = *p->xndx;
	length = ftp->flen;
					/* Multiply ndx by denormalisation 
					 * factor.  
					 * 
					 * xbmul is 1 or table length depending
					 * on state of ixmode.
					 *
					 * Add in the offset, which has already
					 * been denormalised by tblchk().  
					 */

	ndx    = (ndx * p->xbmul) + p->offset;

					/* See notes in ktable() about this
					 * conversion to a 32 bit integer.
					 */
	indx = (long) floor(ndx);

					/* We need to generate a fraction - 
					 * How much above indx is ndx?
					 * It will be between 0 and just
					 * below 1.0.
					 */
	fract = ndx - indx;
			
					/* Start of changes to fix non-
					 * wrap bug.
					 *
					 * There are two changes here:
					 *
					 * 1 - We only run the wrap code
					 * if iwrap = 1. Previously it was
					 * always run.
					 *
					 * 2 - The other change is similar in 
					 * concept to limiting the index to 
					 * (length - 1) when in non-wrap
					 * mode.
					 *
					 * This would be fine - the 
					 * fractional code would enable us
					 * to interpolate using an index
					 * value which is almost as high as
					 * the length of the table.  This
					 * would be good for 7.99999 etc.
					 * However, to be a little pedantic,
					 * what we want is for any index of
					 * 8 or more to produce a result 
					 * exactly equal to the value at the 
					 * guard point.
					 *
					 * We will let all (non negative) 
					 * values which are less than length
					 * pass through. This deals with all
					 * cases 0 to 7.9999 . . . 
					 *
					 * However we will look for final
					 * indexes of length (8) and above and 
					 * take the following steps:
					 * 
					 * fract = 1	
					 * indx = length - 1
					 * 
					 * We then continue with the rest of 
					 * code.  This causes the result to be
					 * the value read from the guard point
					 * - which is what we want.
					 *
					 * Likewise, if the final index is
					 * negative, set both fract and indx
					 * to 0.	
					 */
	if (!p->wrap) 
	{
		if (ndx > length) {
			indx  = length - 1;
			fract = 1;
		}

		else if (ndx < 0) {
			indx  = 0L;
			fract = 0;
		}	
	}
					/* We are in wrap mode, so do the 
					 * wrap function.
				  	 */	
	else	indx &= ftp->lenmask;

					/* Now read the value at indx and the
					 * one beyond.  Code slightly tweaked
					 * for speed by auto incrementing
					 * indx to the next location, rather
					 * than adding one to it.  
					 */
	v1 = *(ftp->ftable + indx++);
	v2 = *(ftp->ftable + indx); 
					/* Calculate the interpolated value and
					 * write it to the result destination.
					 */

	*p->rslt = v1 + (v2 - v1) * fract;
}


					/*************************************/
					
					/* tabli()
					 */

					/* tabli() is similar to ktabli() 
					 * above, except that it processes
					 * an array of input indexes, to 
					 * send results to another array.
					 *
					 * See the notes on ktablei() for 
					 * details of how it works.  Only 
					 * array specific things are 
					 * commented here.
					 */
					 
void tabli(TABLE  *p)
{
					/* Local variables as for ktabli, 
					 * with the following addtions:
					 *
					 * mask		Local 32 bit integer
					 *		to save time in the 
					 *		loop, instead of
					 *		accessing it from
					 *		structure FUNC.
					 */

register FUNC 	*ftp;
register long	indx, mask, length;
register int	nsmps = ksmps;
					/* Three pointers to floats as in 
					 * table() above.
					 */
register float 	*rslt, *pxndx, *tab;
					/* Four floats as per ktabli().
				  	 */
register float 	v1, v2, fract, ndx;
					
					/* Two floats as per table(). These
					 * are for speed - better to have them
					 * local than via a structure.
					 */	
register float	xbmul, offset;
					/*-----------------------------------*/

					/* Assume that TABLE variables have
					 * been set so everything is ready to 
					 * go!
					 */
	
					/* Set up local variables 
					 */
	ftp    = p->ftp;
	rslt   = p->rslt;
	length = ftp->flen;
	pxndx  = p->xndx;
	xbmul  = p->xbmul;
	offset = p->offset;
	mask   = ftp->lenmask;
	tab    = ftp->ftable;
					/* Loop for ksmps cycles.
					 */

	do {
					/* Read in the next raw index and 
					 * increment the pointer ready for
					 * the next cycle.
					 *
					 * Then multiply the ndx by the 
					 * denormalising factor and add in
					 * the offset.
					 */ 

		ndx = (*pxndx++ * xbmul) + offset;

					/* See notes in ktable() about this
					 * conversion to a 32 bit integer.
					 */
		indx = (long) floor(ndx);

					/* We need to generate a fraction - 
					 * How much above indx is ndx?
					 * It will be between 0 and just
					 * below 1.0.
					 */
		fract = ndx - indx;
					/* As for ktabli() code to handle
					 * non wrap mode, and wrap mode.
					 */
		if (!p->wrap) {
			if (ndx > length) {
				indx  = length - 1;
				fract = 1;

			}

			else if (ndx < 0) {
 
				indx  = 0L;
				fract = 0;	
			}
		}
	
		else	indx &= mask;

					/* As for ktabli(), read two values
					 * and interpolate between them.
					 */
		v1 = *(tab + indx++);
		v2 = *(tab + indx);

		*rslt++ = v1 + (v2 - v1)*fract;
	} 
	while(--nsmps);
}

					/*************************************/
					
					/* Four functions to call the above
					 * four, after handling the k rate
					 * table number variable.
					 *
					 * Prior to these running, we can
					 * assume that tblsetkt() has been
					 * run at init time.
					 *
					 * tblsetkt() does very little - 
					 * just setting up the wrap variable
					 * in TABLE. All the other variables
					 * depend on the table number. This
					 * is not available at init time,
					 * so the following 4 functions must
					 * look for the changed table number
					 * and set up the variables accordingly
					 * - generating error messages in a 
					 * way which works at performance time.
					 *
					 * k rate   a rate
					 *
					 * ktablekt tablekt   Non interpolated
					 * ktablikt tablikt   Interpolated
					 * 
					 * Since these perform identical 
					 * operations, apart from the 
					 * function they call, create a
					 * common function to do this work:
					 *
					 * ftkrchk()
					 */
	
int ftkrchk(TABLE *p)
{
					/* Check the table number is >= 1.
					 * Print error and deactivate 
					 * if it is not.
					 * Return 0 to tell calling function
					 * not to proceed with a or k rate
					 * operations.
					 *
					 * We must do this to catch the 
					 * situation where the first call
					 * has a table number of 0, and since
					 * that equals pfn, we would otherwise
					 * proceed without checking the
					 * table number - and none of the
					 * pointers would have been set up.
					 */
	if (*p->xfn < 1) 
	{		
		sprintf(errmsg, "k rate function table no. %f < 1", *p->xfn);
		perferror(errmsg);
		return (NULL);
	}
					/* Check to see if table number 
					 * has changed from previous value.
					 *
					 * On the first run through, the 
					 * previous value will be 0.
					 */


	if (p->pfn != (long)*p->xfn)
	{
					/* If it is different, check to see
					 * if the table exists.
					 *
					 * If it doesn't, an error message
					 * should be produced by ftfindp()
					 * which should also deactivate 
					 * the instrument.
					 *
					 * Return 0 to tell calling function
					 * not to proceed with a or k rate
					 * operations.
					 *
					 * ftfindp is in a new version of 
					 * fgens.c. A prototype for it
					 * should be added to prototype.h.
					 */

		if ( (p->ftp = ftfindp(p->xfn) ) == NULL) 
		{
			return (0);
		}	

					/* p->ftp now points to the FUNC
					 * data structure of the newly 
					 * selected table.
					 * 
					 * Now we set up some variables in 
					 * TABLE ready for the k or a rate
					 * functions which follow.
					 */

					/* Write the integer version of the
					 * table number into pfn so we
					 * can later decide whether subsequent
					 * calls to the k and a rate functions
					 * occur with a table number value 
					 * which points to a different table. 
					 *
					 * p->pfn is an integer.
					 */
		p->pfn = *p->xfn;

					/* Set denormalisation factor to 1 or 
					 * table length, depending on the 
					 * state of ixmode.
					 * 1L means a 32 bit 1.
					 */
		if (*p->ixmode)
			p->xbmul = p->ftp->flen;
	
		else	p->xbmul = 1L;

					/* Multiply the ixoff value by the 
					 * xbmul denormalisation factor and 
					 * then check it is between 0 and the
					 * table length.
					 */

		if (    (p->offset = p->xbmul * *p->ixoff) < 0. 
		     || p->offset > p->ftp->flen
  	   	   ) 
		{
			sprintf(errmsg, "Offset %f < 0 or > tablelength", p->offset);
			perferror(errmsg);
			return(NULL);
		}
					/* If all is well, return 1 to 
					 * tell calling function to proceed
					 * with a or k rate operations.
					 */
		return(1);
	}
}

					/* Now for the four functions, which
					 * are called as a result of being
					 * listed in opcodlst in entry.c
					 */

void	ktablekt(TABLE *p)
{
	if ( ftkrchk(p) ) ktable(p);
}

void	tablekt(TABLE *p)
{
	if ( ftkrchk(p) ) table(p);
}

void	ktablikt(TABLE *p)
{
	if ( ftkrchk(p) ) ktabli(p);
}

void	tablikt(TABLE *p)
{
	if ( ftkrchk(p) ) tabli(p);
}

					/* Re-entering comment free zone . . .
					 */

/*****************************************************************************/

void ko1set(OSCIL1 *p)
{
register FUNC	*ftp;

	if ((ftp = ftfind(p->ifn)) == NULL)
		return;
	if (*p->idur <= fzero)
		warning("duration < zero");
	p->ftp = ftp;
	p->phs = 0;
	p->dcnt = *p->idel * ekr;
	p->kinc = (long) (kicvt / *p->idur);
}

void kosc1(OSCIL1 *p)
{
register FUNC *ftp;
register long  phs, dcnt;

	ftp = p->ftp;
	phs = p->phs;
	*p->rslt = *(ftp->ftable + (phs >> ftp->lobits)) * *p->kamp;
	if ((dcnt = p->dcnt) > 0L)
		dcnt--;
	else if (dcnt == 0L) {
		phs += p->kinc;
		if (phs >= MAXLEN) {
			phs = MAXLEN;
			dcnt--;
		}
		p->phs = phs;
	}
	p->dcnt = dcnt;
}

void kosc1i(OSCIL1  *p)
{
register FUNC	*ftp;
register float	fract, v1, *ftab;
register long	phs, dcnt;

	ftp = p->ftp;
	phs = p->phs;
	fract = PFRAC(phs); 	
	ftab = ftp->ftable + (phs >> ftp->lobits);
	v1 = *ftab++;
	*p->rslt = (v1 + (*ftab - v1) * fract) * *p->kamp;
	if ((dcnt = p->dcnt) > 0L) {
		dcnt--;
		p->dcnt = dcnt;
	}
	else if (dcnt == 0L) {
		phs += p->kinc;
		if (phs >= MAXLEN) {
			phs = MAXLEN;
			dcnt--;
			p->dcnt = dcnt;
		}
		p->phs = phs;
	}
}

void oscset(OSC *p)
{
register FUNC	*ftp;

	if ((ftp = ftfind(p->ifn)) != NULL) {
		p->ftp = ftp;
		if (*p->iphs >= 0)
			p->lphs = ((long)(*p->iphs * fmaxlen)) & PMASK;
	}
}

void koscil(OSC *p)
{
register FUNC	*ftp;
register long	phs, inc;

	ftp = p->ftp;
	phs = p->lphs;
	inc = *p->xcps * kicvt;
	*p->sr = *(ftp->ftable + (phs >> ftp->lobits)) * *p->xamp;
	phs += inc;
	phs &= PMASK;
	p->lphs = phs;
}

void osckk(OSC *p)
{
register FUNC	*ftp;
register float	amp, *ar, *ftbl;
register long	phs, inc, lobits;
register int	nsmps = ksmps;

	ftp = p->ftp;
	ftbl = ftp->ftable;
	phs = p->lphs;
	inc = *p->xcps * sicvt;
	lobits = ftp->lobits;
	amp = *p->xamp;
	ar = p->sr;
	do {
		*ar++ = *(ftbl + (phs >> lobits)) * amp;
		phs += inc;
		phs &= PMASK;
	}
	while (--nsmps);
	p->lphs = phs;
}

void oscka(OSC *p)
{
register FUNC	*ftp;
register float	*ar, amp, *cpsp, *ftbl;
register long	phs, lobits;
register int	nsmps = ksmps;

	ftp = p->ftp;
	ftbl = ftp->ftable;
	lobits = ftp->lobits;
	amp = *p->xamp;
	cpsp = p->xcps;
	phs = p->lphs;
	ar = p->sr;
	do {
		register long inc;
		inc = *cpsp++ * sicvt;
		*ar++ = *(ftbl + (phs >> lobits)) * amp;
		phs += inc;
		phs &= PMASK;
	}
	while (--nsmps);
	p->lphs = phs;
}

void oscak(OSC *p)
{
register FUNC	*ftp;
register float	*ar, *ampp, *ftbl;
register long	phs, inc, lobits;
register int	nsmps = ksmps;

	ftp = p->ftp;
	ftbl = ftp->ftable;
	lobits = ftp->lobits;
	phs = p->lphs;
	inc = *p->xcps * sicvt;
	ampp = p->xamp;
	ar = p->sr;
	do {
		*ar++ = *(ftbl + (phs >>lobits)) * *ampp++;
		phs += inc;
		phs &= PMASK;
	}
	while (--nsmps);
	p->lphs = phs;
}

void oscaa(OSC *p)
{
register FUNC	*ftp;
register float	*ar, *ampp, *cpsp, *ftbl;
register long	phs, lobits;
register int	nsmps = ksmps;

	ftp = p->ftp;
	ftbl = ftp->ftable;
	lobits = ftp->lobits;
	phs = p->lphs;
	ampp = p->xamp;
	cpsp = p->xcps;
	ar = p->sr;
	do {
		register long inc;
		inc = *cpsp++ * sicvt;
		*ar++ = *(ftbl + (phs >>lobits)) * *ampp++;
		phs += inc;
		phs &= PMASK;
	}
	while (--nsmps);
	p->lphs = phs;
}

void koscli(OSC  *p)
{
register FUNC	*ftp;
register long	phs, inc;
register float  *ftab, fract, v1;

	phs = p->lphs;
	ftp = p->ftp;
	fract = PFRAC(phs);
	ftab = ftp->ftable + (phs >> ftp->lobits);
	v1 = *ftab++;
	*p->sr = (v1 + (*ftab - v1) * fract) * *p->xamp;
	inc = *p->xcps * kicvt;
	phs += inc;
	phs &= PMASK;
	p->lphs = phs;
}

void osckki(OSC  *p)
{
register FUNC	*ftp;
register float	fract, v1, amp, *ar, *ftab;
register long	phs, inc, lobits;
register int	nsmps = ksmps;

	ftp = p->ftp;
	lobits = ftp->lobits;
	phs = p->lphs;
	inc = *p->xcps * sicvt;
	amp = *p->xamp;
	ar = p->sr;
	do {
		fract = PFRAC(phs);
		ftab = ftp->ftable + (phs >> lobits);
		v1 = *ftab++;
		*ar++ = (v1 + (*ftab - v1) * fract) * amp;
		phs += inc;
		phs &= PMASK;
	}
	while (--nsmps);
	p->lphs = phs;
}

void osckai(OSC  *p)
{
register FUNC	*ftp;
register float	*ar, amp, *cpsp, fract, v1, *ftab;
register long	phs, lobits;
register int	nsmps = ksmps;

	ftp = p->ftp;
	lobits = ftp->lobits;
	amp = *p->xamp;
	cpsp = p->xcps;
	phs = p->lphs;
	ar = p->sr;
	do {
		register long inc;
		inc = *cpsp++ * sicvt;
		fract = PFRAC(phs);
		ftab = ftp->ftable + (phs >> lobits);
		v1 = *ftab++;
		*ar++ = (v1 + (*ftab - v1) * fract) * amp;
		phs += inc;
		phs &= PMASK;
	}
	while (--nsmps);
	p->lphs = phs;
}

void oscaki(OSC  *p)
{
register FUNC	*ftp;
register float	v1, fract, *ar, *ampp, *ftab;
register long	phs, inc, lobits;
register int	nsmps = ksmps;

	ftp = p->ftp;
	ftab = ftp->ftable;
	lobits = ftp->lobits;
	phs = p->lphs;
	inc = *p->xcps * sicvt;
	ampp = p->xamp;
	ar = p->sr;
	do {
		fract = PFRAC(phs);
		ftab = ftp->ftable + (phs >> lobits);
		v1 = *ftab++;
		*ar++ = (v1 + (*ftab - v1) * fract) * *ampp++;
		phs += inc;
		phs &= PMASK;
	}
	while (--nsmps);
	p->lphs = phs;
}

void oscaai(OSC  *p)
{
register FUNC	*ftp;
register float	v1, fract, *ar, *ampp, *cpsp, *ftab;
register long	phs, lobits;
register int	nsmps = ksmps;

	ftp = p->ftp;
	ftab = ftp->ftable;
	lobits = ftp->lobits;
	phs = p->lphs;
	ampp = p->xamp;
	cpsp = p->xcps;
	ar = p->sr;
	do {
		register long inc;
		inc = *cpsp++ * sicvt;
		fract = PFRAC(phs);
		ftab = ftp->ftable + (phs >> lobits);
		v1 = *ftab++;
		*ar++ = (v1 + (*ftab - v1) * fract) * *ampp++;
		phs += inc;
		phs &= PMASK;
	}
	while (--nsmps);
	p->lphs = phs;
}

