/*
 * Copyright (c) 1983-2013 Trevor Wishart and Composers Desktop Project Ltd
 * http://www.trevorwishart.co.uk
 * http://www.composersdesktop.com
 *
 This file is part of the CDP System.

    The CDP System is free software; you can redistribute it
    and/or modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    The CDP System is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with the CDP System; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA
 *
 */



/* floatsams version*/
/*RWD Apr 2011 corrected usage message. Rebuilt with fixed (?) library for extra array element */
#include <stdio.h>
#include <stdlib.h>
#include <structures.h>
#include <cdpmain.h>
#include <tkglobals.h>
#include <pnames.h>
#include <filters.h>
#include <processno.h>
#include <modeno.h>
#include <globcon.h>
#include <logic.h>
#include <filetype.h>
#include <mixxcon.h>
#include <arrays.h>
#include <flags.h>
#include <speccon.h>
#include <arrays.h>
#include <special.h>
#include <filters1.h>
#include <formants.h>
#include <sfsys.h>
#include <osbind.h>
#include <string.h>
#include <math.h>
#include <srates.h>
/*RWD April 2004*/
#include <filtcon.h>

#ifndef cdp_round
extern int cdp_round(double a);
#endif

#if defined unix || defined __GNUC__
#define round(x) lround((x))
#endif

/********************************************************************************************/
/********************************** FORMERLY IN buffers.c ***********************************/
/********************************************************************************************/

static int  create_fltiter_buffer(dataptr dz);

/********************************************************************************************/
/********************************** FORMERLY IN specialin.c *********************************/
/********************************************************************************************/

static int  read_filter_data(char *filename,dataptr dz);
static int  read_time_varying_filter_data(char *filename,dataptr dz);
static int  get_data_from_tvary_infile(char *filename,dataptr dz);
static int  getmaxlinelen(int *maxcnt,FILE *fp);
static int  check_filter_data(int wordcnt_in_line,dataptr dz);
static int  check_seq_and_range_of_filter_data(double *fbrk,int wordcnt_in_line,dataptr dz);

static int	check_seq_and_range_of_filter_data2(double *fbrk,double *hbrk,dataptr dz);
static int	getmaxlinelen2(int *maxcnt,FILE *fp);
static int	read_data_from_t_and_p_vary_infile(char *filename,dataptr dz);

/***************************************************************************************/
/****************************** FORMERLY IN aplinit.c **********************************/
/***************************************************************************************/

/***************************** ESTABLISH_BUFPTRS_AND_EXTRA_BUFFERS **************************/

int establish_bufptrs_and_extra_buffers(dataptr dz)
{
//	int is_spec = FALSE;
	dz->extra_bufcnt = -1;	/* ENSURE EVERY CASE HAS A PAIR OF ENTRIES !! */
	dz->bptrcnt = 0;
	dz->bufcnt  = 0;
	switch(dz->process) {
	case(EQ):
	case(LPHP):
	case(FSTATVAR):
	case(FLTBANKN):
	case(FLTBANKC):
	case(FLTBANKU):
	case(FLTBANKV):
	case(FLTBANKV2):
	case(FLTSWEEP):
	case(ALLPASS):				dz->extra_bufcnt = 0;	dz->bufcnt = 1;		break;

	case(FLTITER):				dz->extra_bufcnt = 0;	dz->bufcnt = 4;		break;
	case(MAKE_VFILT):			dz->extra_bufcnt = 0;	dz->bufcnt = 0;		break;
	default:
		sprintf(errstr,"Unknown program type [%d] in establish_bufptrs_and_extra_buffers()\n",dz->process);
		return(PROGRAM_ERROR);
	}

	if(dz->extra_bufcnt < 0) {
		sprintf(errstr,"bufcnts have not been set: establish_bufptrs_and_extra_buffers()\n");
		return(PROGRAM_ERROR);
	}
	return establish_groucho_bufptrs_and_extra_buffers(dz);
}

/***************************** SETUP_INTERNAL_ARRAYS_AND_ARRAY_POINTERS **************************/

int setup_internal_arrays_and_array_pointers(dataptr dz)
{
	int n;		 
	dz->ptr_cnt    = -1;		/* base constructor...process */
	dz->array_cnt  = -1;
	dz->iarray_cnt = -1;
	dz->larray_cnt = -1;
	switch(dz->process) {
	case(FLTBANKC):	dz->array_cnt = 2; dz->iarray_cnt = 0; dz->larray_cnt = 0; dz->ptr_cnt = 0;	dz->fptr_cnt = 0; break;

	case(ALLPASS):	dz->array_cnt = 2; dz->iarray_cnt = 0; dz->larray_cnt = 0; dz->ptr_cnt = 0;	dz->fptr_cnt = 0; break;

//	case(LPHP):		dz->array_cnt = 11; dz->iarray_cnt= 0; dz->larray_cnt = 0; dz->ptr_cnt = 0; dz->fptr_cnt = 0; break;
// TW MULTICHAN 2010
	case(LPHP):		
		dz->array_cnt = 0; dz->iarray_cnt= 0; dz->larray_cnt = 0; dz->ptr_cnt = 0; dz->fptr_cnt = 0; 
		break;
	case(EQ):	   	dz->array_cnt = 4; dz->iarray_cnt = 0; dz->larray_cnt = 0; dz->ptr_cnt = 0;	dz->fptr_cnt = 0; break;
	case(FSTATVAR):	dz->array_cnt = 4; dz->iarray_cnt = 0; dz->larray_cnt = 0; dz->ptr_cnt = 0;	dz->fptr_cnt = 0; break;
	case(FLTSWEEP):	dz->array_cnt = 4; dz->iarray_cnt = 0; dz->larray_cnt = 0; dz->ptr_cnt = 0;	dz->fptr_cnt = 0; break;

	case(FLTBANKN):	dz->array_cnt = 19; dz->iarray_cnt= 0; dz->larray_cnt = 0; dz->ptr_cnt = 0; dz->fptr_cnt = 0; break;
	case(FLTBANKU):	dz->array_cnt = 19; dz->iarray_cnt= 0; dz->larray_cnt = 0; dz->ptr_cnt = 0; dz->fptr_cnt = 0; break;
	case(FLTBANKV):	dz->array_cnt = 19; dz->iarray_cnt= 0; dz->larray_cnt = 1; dz->ptr_cnt = 0; dz->fptr_cnt = 0; break;
	case(FLTBANKV2):dz->array_cnt = 22; dz->iarray_cnt= 0; dz->larray_cnt = 1; dz->ptr_cnt = 0; dz->fptr_cnt = 0; break;
	case(FLTITER):	dz->array_cnt = 19; dz->iarray_cnt= 0; dz->larray_cnt = 0; dz->ptr_cnt = 0; dz->fptr_cnt = 0; break;
	case(MAKE_VFILT): dz->array_cnt= 1; dz->iarray_cnt= 0; dz->larray_cnt = 0; dz->ptr_cnt = 0; dz->fptr_cnt = 0; break;
	}

/*** WARNING ***
ANY APPLICATION DEALING WITH A NUMLIST INPUT: MUST establish AT LEAST 1 double array: i.e. dz->array_cnt = at least 1
**** WARNING ***/


	if(dz->array_cnt < 0 || dz->iarray_cnt < 0 || dz->larray_cnt < 0 || dz->ptr_cnt < 0 || dz->fptr_cnt < 0) {
		sprintf(errstr,"array_cnt not set in setup_internal_arrays_and_array_pointers()\n");	   
		return(PROGRAM_ERROR);
	}

	if(dz->array_cnt > 0) {  
		if((dz->parray  = (double **)malloc(dz->array_cnt * sizeof(double *)))==NULL) {
			sprintf(errstr,"INSUFFICIENT MEMORY for internal double arrays.\n");
			return(MEMORY_ERROR);
		}
		for(n=0;n<dz->array_cnt;n++)
			dz->parray[n] = NULL;
	}
	if(dz->iarray_cnt > 0) {
		if((dz->iparray = (int     **)malloc(dz->iarray_cnt * sizeof(int *)))==NULL) {
			sprintf(errstr,"INSUFFICIENT MEMORY for internal int arrays.\n");
			return(MEMORY_ERROR);
		}
		for(n=0;n<dz->iarray_cnt;n++)
			dz->iparray[n] = NULL;
	}
	if(dz->larray_cnt > 0) {	  
		if((dz->lparray = (int    **)malloc(dz->larray_cnt * sizeof(int *)))==NULL) {
			sprintf(errstr,"INSUFFICIENT MEMORY for internal long arrays.\n");
			return(MEMORY_ERROR);
		}
		for(n=0;n<dz->larray_cnt;n++)
			dz->lparray[n] = NULL;
	}
	if(dz->ptr_cnt > 0)   {  	  
		if((dz->ptr    	= (double  **)malloc(dz->ptr_cnt  * sizeof(double *)))==NULL) {
			sprintf(errstr,"INSUFFICIENT MEMORY for internal pointer arrays.\n");
			return(MEMORY_ERROR);
		}
		for(n=0;n<dz->ptr_cnt;n++)
			dz->ptr[n] = NULL;
	}
	if(dz->fptr_cnt > 0)   {  	  
		if((dz->fptr = (float  **)malloc(dz->fptr_cnt * sizeof(float *)))==NULL) {
			sprintf(errstr,"INSUFFICIENT MEMORY for internal float-pointer arrays.\n");
			return(MEMORY_ERROR);
		}
		for(n=0;n<dz->fptr_cnt;n++)
			dz->fptr[n] = NULL;
	}
	return(FINISHED);
}

/****************************** ASSIGN_PROCESS_LOGIC *********************************/

int assign_process_logic(dataptr dz)
{						 
	switch(dz->process) {
	case(EQ):			  setup_process_logic(SNDFILES_ONLY,		UNEQUAL_SNDFILE,	SNDFILE_OUT,	dz);	break;
	case(LPHP):			  setup_process_logic(SNDFILES_ONLY,		UNEQUAL_SNDFILE,	SNDFILE_OUT,	dz);	break;
	case(FSTATVAR):		  setup_process_logic(SNDFILES_ONLY,		UNEQUAL_SNDFILE,	SNDFILE_OUT,	dz);	break;
	case(FLTBANKN):		  setup_process_logic(SNDFILES_ONLY,		UNEQUAL_SNDFILE,	SNDFILE_OUT,	dz);	break;
	case(FLTBANKC):		  setup_process_logic(SNDFILES_ONLY,    	TO_TEXTFILE,		TEXTFILE_OUT,	dz);	break;
	case(FLTBANKU):		  setup_process_logic(SNDFILES_ONLY,		UNEQUAL_SNDFILE,	SNDFILE_OUT,	dz);	break;
	case(FLTBANKV2):
	case(FLTBANKV):		  setup_process_logic(SNDFILES_ONLY,		UNEQUAL_SNDFILE,	SNDFILE_OUT,	dz);	break;
	case(FLTITER):		  setup_process_logic(SNDFILES_ONLY,		UNEQUAL_SNDFILE,	SNDFILE_OUT,	dz);	break;
	case(FLTSWEEP):		  setup_process_logic(SNDFILES_ONLY,		UNEQUAL_SNDFILE,	SNDFILE_OUT,	dz);	break;
	case(ALLPASS):		  setup_process_logic(SNDFILES_ONLY,		UNEQUAL_SNDFILE,	SNDFILE_OUT,	dz);	break;
	case(MAKE_VFILT):	  setup_process_logic(WORDLIST_ONLY,		TO_TEXTFILE,		NO_OUTPUTFILE,	dz);	break;
	default:
		sprintf(errstr,"Unknown process: assign_process_logic()\n");
		return(PROGRAM_ERROR);
		break;
	}
	if(dz->has_otherfile) {
		switch(dz->input_data_type) {
		case(ALL_FILES):
		case(TWO_SNDFILES):
		case(SNDFILE_AND_ENVFILE):
		case(SNDFILE_AND_BRKFILE):
		case(SNDFILE_AND_UNRANGED_BRKFILE):
		case(SNDFILE_AND_DB_BRKFILE):
			break;
		case(MANY_SNDFILES):
			if(dz->process==INFO_TIMELIST)
				break;
			/* fall thro */
		default:
			sprintf(errstr,"Most processes accepting files with different properties\n"
						   "can only take 2 sound infiles.\n");
			return(PROGRAM_ERROR);
		}
	}
	return(FINISHED);
}

/***************************** SET_LEGAL_INFILE_STRUCTURE **************************
 *
 * Allows 2nd infile to have different props to first infile.
 */

void set_legal_infile_structure(dataptr dz)
{
	switch(dz->process) {
	default:
		dz->has_otherfile = FALSE;
		break;
	}
}

/***************************************************************************************/
/****************************** FORMERLY IN internal.c *********************************/
/***************************************************************************************/

/****************************** SET_LEGAL_INTERNALPARAM_STRUCTURE *********************************/

int set_legal_internalparam_structure(int process,int mode,aplptr ap)
{
	int exit_status = FINISHED;

	switch(process) {
	case(EQ):  	 	exit_status = set_internalparam_data( "000dddiddd",ap);			break;	   
	case(LPHP):    	exit_status = set_internalparam_data(  "000didi",ap);	  		break;	   
	case(FSTATVAR):	exit_status = set_internalparam_data("0000diiiiididd",ap);		break;
	case(FLTBANKN):	exit_status = set_internalparam_data( "000diiiiid",ap);			break;
	case(FLTBANKC):	exit_status = set_internalparam_data( "000di",ap);				break;
	case(FLTBANKU):	exit_status = set_internalparam_data("0000diiiiid",ap);			break;
	case(FLTBANKV):	exit_status = set_internalparam_data("00iidiiiiididiiid",ap);	break;
	case(FLTBANKV2):exit_status = set_internalparam_data("i000iidiiiiididiiidiidd",ap);	break;
	case(FLTITER):	exit_status = set_internalparam_data(    "diiiiiiiiiiid",ap);	break;
	case(FLTSWEEP): exit_status = set_internalparam_data( "000diiiiiddddddddd",ap);	break;
	case(ALLPASS):	exit_status = set_internalparam_data(  "000diiiiiid",ap);		break;
	case(MAKE_VFILT):	break;
	default:
		sprintf(errstr,"Unknown process in set_legal_internalparam_structure()\n");
		return(PROGRAM_ERROR);
	}
	return(exit_status);		
}

/***************************************************************************************/
/******************************* FORMERLY IN sndlib.c **********************************/
/***************************************************************************************/

/*********************** GET_COEFFS1 *************************/

void get_coeffs1(int n,dataptr dz)
{
	dz->parray[FLT_WW][n]   = 2.0 * PI * dz->parray[FLT_FRQ][n] * dz->param[FLT_INV_SR];
	dz->parray[FLT_COSW][n] = cos(dz->parray[FLT_WW][n]);
	dz->parray[FLT_SINW][n] = sin(dz->parray[FLT_WW][n]);
}

/*********************** GET_COEFFS2 ***************************/

void get_coeffs2(int n,dataptr dz)
{
	double g, r;
	r    = exp( -(dz->parray[FLT_WW][n])/(2.0 * dz->param[FLT_Q]));
	dz->parray[FLT_A][n] = 2.0 * r * dz->parray[FLT_COSW][n];
	dz->parray[FLT_B][n] = -(r) * r;
	g    = 1.0 / ((1.0  + dz->parray[FLT_B][n]) * dz->parray[FLT_SINW][n]);
	dz->parray[FLT_AMPL][n] = dz->parray[FLT_AMP][n]/g;
	if(dz->vflag[FLT_DBLFILT])
		dz->parray[FLT_AMPL][n] /= g;
}

/********************************************************************************************/
/********************************** FORMERLY IN specialin.c *********************************/
/********************************************************************************************/

/********************** READ_SPECIAL_DATA ************************/

int read_special_data(char *str,dataptr dz)	   
{
//	int exit_status = FINISHED;
	aplptr ap = dz->application;

	switch(ap->special_data) {
	case(FILTERBANK):				return read_filter_data(str,dz);
	case(TIMEVARYING_FILTERBANK):	return read_time_varying_filter_data(str,dz);
	case(TIMEVARY2_FILTERBANK):		return read_data_from_t_and_p_vary_infile(str,dz);
	default:
		sprintf(errstr,"Unknown special_data type: read_special_data()\n");
		return(PROGRAM_ERROR);
	}
	return(FINISHED);	/* NOTREACHED */
}

/************************** READ_FILTER_DATA ********************************/

int read_filter_data(char *filename,dataptr dz)
{
	int exit_status;
	int n = 0, valcnt;
	char temp[200], *p, *thisword;
	double *frq, *amp;
	int is_frq = TRUE;

	if((dz->fp = fopen(filename,"r"))==NULL) {
		sprintf(errstr,"Cannot open datafile %s\n",filename);
		return(DATA_ERROR);
	}
	while(fgets(temp,200,dz->fp)!=NULL) {
		p = temp;
		if(is_an_empty_line_or_a_comment(p))
			continue;
		n++;
	}
	if(n==0) {
		sprintf(errstr,"No data in file %s\n",filename);
		return(DATA_ERROR);
	}
	dz->iparam[FLT_CNT] = n;
	if((exit_status = allocate_filter_frq_amp_arrays(dz->iparam[FLT_CNT],dz))<0)
		return(exit_status);
	frq = dz->parray[FLT_FRQ];
	amp = dz->parray[FLT_AMP];
	if(fseek(dz->fp,0,0)<0) {
		sprintf(errstr,"fseek() failed in read_filter_data()\n");
		return(SYSTEM_ERROR);
	}
	n = 0;
	while(fgets(temp,200,dz->fp)!=NULL) {
		p = temp;
		if(is_an_empty_line_or_a_comment(temp))
			continue;
		if(n >= dz->iparam[FLT_CNT]) {
			sprintf(errstr,"Accounting problem reading Frq & Amp: read_filter_data()\n");
			return(PROGRAM_ERROR);
		}
		valcnt = 0;
		while(get_word_from_string(&p,&thisword)) {
			if(valcnt>=2) {
				sprintf(errstr,"Too many values on line %d: file %s\n",n+1,filename);
				return(DATA_ERROR);
			}
			if(is_frq) {
				if(sscanf(thisword,"%lf",&(frq[n]))!=1) {
					sprintf(errstr,"Problem reading Frq data: line %d: file %s\n",n+1,filename);
					return(DATA_ERROR);
				}
				if(frq[n]<dz->application->min_special || frq[n]>dz->application->max_special) {
					sprintf(errstr,"frq (%.3lf) on line %d out of range (%.1lf to %.1lf):file %s\n",
					frq[n],n+1,dz->application->min_special,dz->application->max_special,filename);
					return(DATA_ERROR);
				}
				if(dz->mode==FLT_MIDI)
					frq[n] = miditohz(frq[n]);
			} else {
				if((exit_status = get_level(thisword,&(amp[n])))<0)
					return(exit_status);
				if(amp[n]<FLT_MINGAIN || amp[n]>FLT_MAXGAIN) {
					sprintf(errstr,"amp (%lf) out of range (%lf to %.1lf) on line %d: file %s\n",
					amp[n],FLT_MINGAIN,FLT_MAXGAIN,n+1,filename);
					return(DATA_ERROR);
				}
			}
			is_frq = !is_frq;
			valcnt++;
		}
		if(valcnt<2) {
			sprintf(errstr,"Not enough values on line %d: file %s\n",n+1,filename);
			return(DATA_ERROR);
		}
		n++;
	}
	if(fclose(dz->fp)<0) {
		fprintf(stdout,"WARNING: Failed to close input textfile %s.\n",filename);
		fflush(stdout);
	}
	return(FINISHED);
}

/**************************** ALLOCATE_FILTER_FRQ_AMP_ARRAYS *******************************/

int allocate_filter_frq_amp_arrays(int fltcnt,dataptr dz)
{
//	int chans = dz->infile->channels; 
	/*RWD 9:2001 must have empty arrays */
	if((dz->parray[FLT_AMP] = (double *)calloc(fltcnt * sizeof(double),sizeof(char)))==NULL
	|| (dz->parray[FLT_FRQ] = (double *)calloc(fltcnt * sizeof(double),sizeof(char)))==NULL) {
		sprintf(errstr,"INSUFFICIENT MEMORY for filter amp and frq arrays.\n");
		return(MEMORY_ERROR);
	}
	return(FINISHED);
}

/************************** READ_TIME_VARYING_FILTER_DATA ********************************/

int read_time_varying_filter_data(char *filename,dataptr dz)
{
	int exit_status;
//	int cnt = 0;
	if((exit_status = get_data_from_tvary_infile(filename,dz))<0)
		return(exit_status);
	if((exit_status = check_filter_data(dz->iparam[FLT_ENTRYCNT],dz))<0)
		return(exit_status);
	return(FINISHED);
}

/**************************** GET_DATA_FROM_TVARY_INFILE *******************************/

int get_data_from_tvary_infile(char *filename,dataptr dz)
{
	int exit_status;
	char *temp, *p, *thisword;
	int maxlinelen, frqcnt;
	int total_wordcnt = 0;
	int  columns_in_this_row, columns_in_row = 0, number_of_rows = 0;
/* NEW CODE */
	int n, m, k, old_wordcnt;
	double filedur = (double)(dz->insams[0]/dz->infile->channels)/(double)(dz->infile->srate);
	double far_time = filedur + FLT_TAIL + 1.0;
/* NEW CODE */

	double val;
	if((dz->fp = fopen(filename,"r"))==NULL) {
		sprintf(errstr,"Cannot open datafile %s\n",filename);
		return(DATA_ERROR);
	}
	if((exit_status = getmaxlinelen(&maxlinelen,dz->fp))<0)
		return(exit_status);
	if((fseek(dz->fp,0,0))<0) {
		sprintf(errstr,"Seek failed in get_data_from_tvary_infile()\n");
		return(SYSTEM_ERROR);
	}
	if((temp = (char *)malloc((maxlinelen+2) * sizeof(char)))==NULL) {
		sprintf(errstr,"INSUFFICIENT MEMORY for temporary line storage.\n");
		return(MEMORY_ERROR);
	}
	while(fgets(temp,maxlinelen,dz->fp)!=NULL) {
		columns_in_this_row = 0;
		if(is_an_empty_line_or_a_comment(temp))
			continue;
		p = temp;
		while(get_word_from_string(&p,&thisword)) { 
			if((exit_status = get_level(thisword,&val))<0) {	/* reads vals or dB vals */
				free(temp);
				return(exit_status);
			}
			if((total_wordcnt==0 && ((dz->parray[FLT_FBRK] = (double *)malloc(sizeof(double)))==NULL))
			|| (dz->parray[FLT_FBRK] = (double *)realloc(dz->parray[FLT_FBRK],(total_wordcnt+1) * sizeof(double)))==NULL) {
				free(temp);
				sprintf(errstr,"INSUFFICIENT MEMORY to reallocate filter brktable.\n");
				return(MEMORY_ERROR);
			}
			dz->parray[FLT_FBRK][total_wordcnt] = val;
			columns_in_this_row++;
			total_wordcnt++;
		}
		if(number_of_rows==0) {
			if((columns_in_row = columns_in_this_row)<3) {
				sprintf(errstr,"Insufficient filter data in row 1 of file %s.\n",filename);
				free(temp);
				return(DATA_ERROR);
			} else if (ODD(columns_in_row - 1)) {
				sprintf(errstr,"Frq and Amp data not paired correctly (or no Time) in row 1 of file %s.\n",filename);
				free(temp);
				return(DATA_ERROR);
			}
		} else if(columns_in_this_row!=columns_in_row) {
			free(temp);
			if(columns_in_this_row < columns_in_row)
				sprintf(errstr,"Not enough entries in row %d of file %s\n",number_of_rows+1,filename);
			else
				sprintf(errstr,"Too many entries in row %d of file %s\n",number_of_rows+1,filename);
			return(DATA_ERROR);
		}
		number_of_rows++;
	}
	dz->iparam[FLT_WORDCNT] = total_wordcnt;
	free(temp);
	if(columns_in_row<3) {
		sprintf(errstr,"Insufficient data in each row, to define filters.\n");
		return(DATA_ERROR);
	}
	dz->iparam[FLT_ENTRYCNT] = columns_in_row;
	frqcnt = columns_in_row - 1;
	if(ODD(frqcnt)) {
		sprintf(errstr,"amplitude and freq data not correctly paired in rows.\n");
		return(DATA_ERROR);
	}
	dz->iparam[FLT_CNT]       = frqcnt/2;
	dz->iparam[FLT_TIMESLOTS] = number_of_rows;
	if(fclose(dz->fp)<0) {
		fprintf(stdout,"WARNING: Failed to close input textfile %s.\n",filename);
		fflush(stdout);
	}
/* NEW CODE */
	if(dz->parray[FLT_FBRK][0] !=0.0) {
		if(flteq(dz->parray[FLT_FBRK][0],0.0))
			dz->parray[FLT_FBRK][0] = 0.0;
		else {
			dz->iparam[FLT_TIMESLOTS]++;
			old_wordcnt = dz->iparam[FLT_WORDCNT];
			k = old_wordcnt-1;
			dz->iparam[FLT_WORDCNT] += dz->iparam[FLT_ENTRYCNT];
			m = dz->iparam[FLT_WORDCNT] - 1;
			if((dz->parray[FLT_FBRK] = (double *)realloc(dz->parray[FLT_FBRK],dz->iparam[FLT_WORDCNT] * sizeof(double)))==NULL) {
				sprintf(errstr,"INSUFFICIENT MEMORY to reallocate filter brktable.\n");
				return(MEMORY_ERROR);
			}
			for(n=0;n<old_wordcnt;n++,m--,k--)
				dz->parray[FLT_FBRK][m] = dz->parray[FLT_FBRK][k];
			dz->parray[FLT_FBRK][0] = 0.0;
			total_wordcnt = dz->iparam[FLT_WORDCNT];
		}
	}

	if(dz->parray[FLT_FBRK][total_wordcnt - dz->iparam[FLT_ENTRYCNT]] < far_time) {
		dz->iparam[FLT_TIMESLOTS]++;
		m = dz->iparam[FLT_WORDCNT];
		k = m - dz->iparam[FLT_ENTRYCNT];
		dz->iparam[FLT_WORDCNT] += dz->iparam[FLT_ENTRYCNT];
		if((dz->parray[FLT_FBRK] = (double *)realloc(dz->parray[FLT_FBRK],dz->iparam[FLT_WORDCNT] * sizeof(double)))==NULL) {
			sprintf(errstr,"INSUFFICIENT MEMORY to reallocate filter brktable.\n");
			return(MEMORY_ERROR);
		}
		dz->parray[FLT_FBRK][m] = far_time;
		m++;
		k++;
		for(n=1;n<dz->iparam[FLT_ENTRYCNT];n++,m++,k++)
			dz->parray[FLT_FBRK][m] = dz->parray[FLT_FBRK][k];
	}
/* NEW CODE */
	return(FINISHED);
}

/**************************** GETMAXLINELEN *******************************/

int getmaxlinelen(int *maxcnt,FILE *fp)
{
	int thiscnt = 0;
	char c;
	*maxcnt = 0;
	while((c= (char)fgetc(fp))!=EOF) {
		if(c=='\n' || c == ENDOFSTR) {
			*maxcnt = max(*maxcnt,thiscnt);
			thiscnt = 0;
		} else
			thiscnt++;			
	}
	*maxcnt = (int)max(*maxcnt,thiscnt);
	*maxcnt += 4;	/* NEWLINE, ENDOFSTR and safety!! */
	return(FINISHED);
}			

/**************************** CHECK_FILTER_DATA *******************************/

int check_filter_data(int wordcnt_in_line,dataptr dz)
{
	int    exit_status;	
	int   n, lastfilt, new_total_wordcnt;
	double endtime;
	int    total_wordcnt = dz->iparam[FLT_WORDCNT];
	if(dz->parray[FLT_FBRK][0] < 0.0) {
		sprintf(errstr,"Negative time value (%lf) on line 1.\n",dz->parray[FLT_FBRK][0]);
		return(DATA_ERROR);
	}
	if(flteq(dz->parray[FLT_FBRK][0],0.0))		
		dz->parray[FLT_FBRK][0] = 0.0;			/* FORCE A FILTER SETTING AT TIME ZERO */
	else {				 			
		if((dz->parray[FLT_FBRK] = 
		(double *)realloc(dz->parray[FLT_FBRK],(total_wordcnt+wordcnt_in_line) * sizeof(double)))==NULL) {
			sprintf(errstr,"INSUFFICIENT MEMORY to reallocate filter brktable.\n");
			return(MEMORY_ERROR);
		}
		for(n=total_wordcnt-1; n>=0; n--)
			dz->parray[FLT_FBRK][n + wordcnt_in_line] = dz->parray[FLT_FBRK][n];
		total_wordcnt += wordcnt_in_line;
		dz->parray[FLT_FBRK][0] = 0.0;
		dz->iparam[FLT_TIMESLOTS]++;
	}
	dz->iparam[FLT_WORDCNT] = total_wordcnt;
	if((exit_status = check_seq_and_range_of_filter_data(dz->parray[FLT_FBRK],wordcnt_in_line,dz))<0)
		return(exit_status);
								/* FORCE A FILTER SETTING AT (BEYOND) END OF FILE */
	lastfilt = total_wordcnt - wordcnt_in_line;
	endtime =  (double)(dz->insams[0]/dz->infile->channels)/(double)dz->infile->srate;
	if(dz->parray[FLT_FBRK][lastfilt] <= endtime) {
		if((dz->parray[FLT_FBRK] = 
		(double *)realloc(dz->parray[FLT_FBRK],(total_wordcnt + wordcnt_in_line) * sizeof(double)))==NULL) {
			sprintf(errstr,"INSUFFICIENT MEMORY to reallocate filter brktable.\n");
			return(MEMORY_ERROR);
		}
		new_total_wordcnt = total_wordcnt + wordcnt_in_line;
		for(n=total_wordcnt;n<new_total_wordcnt;n++)
			dz->parray[FLT_FBRK][n] = dz->parray[FLT_FBRK][n - wordcnt_in_line];
		dz->parray[FLT_FBRK][total_wordcnt] = endtime + 1.0;
		total_wordcnt = new_total_wordcnt;
		dz->iparam[FLT_TIMESLOTS]++;
	}
	if(dz->iparam[FLT_TIMESLOTS]<2) {
		sprintf(errstr,"Error in timeslot logic: check_filter_data()\n");
		return(PROGRAM_ERROR);
	}
	dz->iparam[FLT_WORDCNT] = total_wordcnt;
	return(FINISHED);
}

/**************************** CHECK_SEQ_AND_RANGE_OF_FILTER_DATA *******************************/

int check_seq_and_range_of_filter_data(double *fbrk,int wordcnt_in_line,dataptr dz)
{
	double lasttime = 0.0;
	int n, m, lineno;
	for(n=1;n<dz->iparam[FLT_WORDCNT];n++) {
		m = n%wordcnt_in_line;
		lineno = (n/wordcnt_in_line)+1;	/* original line-no : ignoring comments */
		if(m==0) {
			if(fbrk[n] <= lasttime) {
				sprintf(errstr,"Time is out of sequence on line %d\n",lineno);
				return(DATA_ERROR);
//NEW AUGUST 2006
//			} else if(fbrk[n] > dz->duration) {
//				fbrk[n] = dz->duration + 1.0;
//				dz->iparam[FLT_WORDCNT] = lineno * wordcnt_in_line;
			}
			lasttime = fbrk[n];
		} else if(ODD(m)) {
			if(fbrk[n]<dz->application->min_special || fbrk[n]>dz->application->max_special) {
				sprintf(errstr,"frq_or_midi value [%.3lf] out of range (%.1f - %.1f) on line %d\n",
				fbrk[n],dz->application->min_special,dz->application->max_special,lineno);
					return(DATA_ERROR);
			}
			if(dz->mode==FLT_MIDI)
				fbrk[n] = miditohz(fbrk[n]);
		} else
			fbrk[n] = max(fbrk[n],MINFILTAMP);	/* Zero filter amp, forced to +ve, but effectively zero */
	}
	return(FINISHED);
}


/********************************************************************************************/
/********************************** FORMERLY IN preprocess.c ********************************/
/********************************************************************************************/

/****************************** PARAM_PREPROCESS *********************************/

int param_preprocess(dataptr dz)	
{
//	int exit_status = FINISHED;

	switch(dz->process) {
	case(EQ):			case(LPHP):			case(FSTATVAR):
	case(FLTBANKN):		case(FLTBANKU):		case(FLTBANKV):
	case(FLTSWEEP):		case(ALLPASS):		case(FLTITER):
	case(FLTBANKC):		case(FLTBANKV2):
		return filter_preprocess(dz);
	case(MAKE_VFILT):	break;
	default:
		sprintf(errstr,"PROGRAMMING PROBLEM: Unknown process in param_preprocess()\n");
		return(PROGRAM_ERROR);
	}
	return(FINISHED);	/* NOTREACHED */
}


/********************************************************************************************/
/********************************** FORMERLY IN procgrou.c **********************************/
/********************************************************************************************/

/**************************** GROUCHO_PROCESS_FILE ****************************/

int groucho_process_file(dataptr dz)   /* FUNCTIONS FOUND IN PROCESS.C */
{	
//	int exit_status = FINISHED;

	if(dz->process!= FLTBANKC)
		display_virtual_time(0L,dz);

	switch(dz->process) {
	case(LPHP):
	case(EQ):	 		case(FSTATVAR):		case(FLTSWEEP):		case(ALLPASS):
	case(FLTBANKN):		case(FLTBANKC):		case(FLTBANKU):		case(FLTBANKV):		
	case(FLTBANKV2):		
	return filter_process(dz);
	case(FLTITER):			
		return iterating_filter(dz);
	case(MAKE_VFILT):
		return make_vfilt_data(dz);
	default:
		sprintf(errstr,"Unknown case in process_file()\n");
		return(PROGRAM_ERROR);
	}
	return(FINISHED);	/* NOTREACHED */
}

/********************************************************************************************/
/********************************** FORMERLY IN pconsistency.c ******************************/
/********************************************************************************************/

/****************************** CHECK_PARAM_VALIDITY_AND_CONSISTENCY *********************************/

int check_param_validity_and_consistency(dataptr dz)
{
//	int exit_status = FINISHED;
	handle_pitch_zeros(dz);
	switch(dz->process) {
	case(EQ):		case(LPHP):		case(FSTATVAR):		case(FLTBANKN):
	case(FLTBANKU):	case(FLTBANKV):	case(FLTSWEEP):		case(ALLPASS):
	case(FLTITER):	case(FLTBANKC):	case(FLTBANKV2):		
		return filter_pconsistency(dz);
	}
	return(FINISHED);
}

/********************************************************************************************/
/********************************** FORMERLY IN buffers.c ***********************************/
/********************************************************************************************/

/**************************** ALLOCATE_LARGE_BUFFERS ******************************/

int allocate_large_buffers(dataptr dz)
{
	switch(dz->process) {
	case(EQ):	   		case(LPHP):			case(FSTATVAR):
	case(FLTBANKN):		case(FLTBANKU):		case(FLTBANKV):		case(FLTBANKV2):
	case(FLTSWEEP):		case(ALLPASS):			
		return create_sndbufs(dz);

	case(FLTITER):			
		return create_fltiter_buffer(dz);
	case(FLTBANKC):			
	case(MAKE_VFILT):
		return(FINISHED);
	default:
		sprintf(errstr,"Unknown program no. in allocate_large_buffers()\n");
		return(PROGRAM_ERROR);
	}
	return(FINISHED);	/* NOTREACHED */
}

/*************************** CREATE_FLTITER_BUFFER **************************
 *
 * (1)	Create extra spaces for interpolation guard points at end of infile.
 *
 * (2) 	Allow for any cumulative addition errors in interpolation.
 *
 * (3)	Output buffer must be at least as big as the overflow buffer.
 *	Output buffer must be big enough for the whole of any possible
 *	data overflow (in overflow_size buff) to be copied back into it.
 *	This is because the overflow buffer is ZEROED after such a copy
 *	and if a 2nd copy of the overflow back into the main buffer
 *	were necessary, we would be copying zeroes rather than true data.
 *
 *	Output buffer must be bigger than the maximum possible delay.
 * 	When write_start is beyond end of buffer, we copy the overflow buffer
 *	data back into the true buffer and reset the pointers by subtracting
 *	buflen (length of output buffer). We must be sure that 'write_start'
 *	will then be located WITHIN the true buffer. In the worst possible
 *	case the write_start pointer is at the very end of the true buffer,
 *	and is then delayed to its maximum possible extent, maxdelay_size (Z),
 *	which will be Z samps into the overflow buffer. After the copy-back,
 *	the 'write_start' pointer must lie WITHIN the true buffer.
 *	Hence the true buflen must be >= maximum delay.
 *
 *		true buffer	    			overflow
 *  |-----------------------------|------------------------------------------|
 *			    			 worst^					    ^
 *		    		 possible case^					    ^
 *		             		      ^----->-delayed by maxdelay_size to ->-----^
 *			 					  ^<-restored by -buffer_size into truebuf-<-^
 *  |<-------- BUFFER_SIZE------->
 *
 */

int create_fltiter_buffer(dataptr dz)
{
	int   big_buffer_size;
	size_t bigbufsize;
	double k;
	int  chans = dz->infile->channels;
	int bufminimum, infilesamps;
	dz->iparam[FLT_INFILESPACE] = dz->insams[0];
	if(dz->param[FLT_PSHIFT]>0.0) {
		dz->iparam[FLT_INFILESPACE] += chans;		/* 1 */
		k = pow(2.0,dz->param[FLT_PSHIFT]);
		dz->iparam[FLT_OVFLWSIZE]  = ((round((dz->insams[0]/chans) * k) * chans) + chans);
		dz->iparam[FLT_OVFLWSIZE] += FLT_SAFETY;						/* 2 */
	} else
		dz->iparam[FLT_OVFLWSIZE] = dz->iparam[FLT_INFILESPACE] + FLT_SAFETY;
	infilesamps      = dz->iparam[FLT_INFILESPACE];
	bufminimum       = dz->iparam[FLT_OVFLWSIZE];
	bigbufsize   = (size_t)Malloc(-1);
	bigbufsize /= sizeof(float);
	bigbufsize  -= (dz->iparam[FLT_INFILESPACE] * 2) + dz->iparam[FLT_OVFLWSIZE];
	if(bigbufsize<bufminimum)
		bigbufsize = bufminimum;	
//TW COMMENT: filters don't sfseek: so old buffer protocol can be abandoned

	dz->buflen = (int) bigbufsize;
	big_buffer_size = (dz->iparam[FLT_INFILESPACE] * 2) + dz->buflen + dz->iparam[FLT_OVFLWSIZE];
	if((dz->bigbuf = (float *) malloc((size_t)(big_buffer_size * sizeof(float)))) == NULL) {
		sprintf(errstr, "INSUFFICIENT MEMORY to create sound buffers.\n");
		return(MEMORY_ERROR);
	}
	dz->sampbuf[0]        	  = dz->bigbuf;
	dz->sampbuf[1]        	  = dz->sampbuf[0] + infilesamps;
	dz->sampbuf[FLT_OUTBUF]   = dz->sampbuf[1] + infilesamps;
	dz->sampbuf[FLT_OVFLWBUF] = dz->sampbuf[FLT_OUTBUF] + dz->buflen;
	memset((char *)dz->bigbuf,0,(size_t)(big_buffer_size * sizeof(float)));
	return(FINISHED);
}


/********************************************************************************************/
/********************************** FORMERLY IN cmdline.c ***********************************/
/********************************************************************************************/

int get_process_no(char *prog_identifier_from_cmdline,dataptr dz)
{
	if     (!strcmp(prog_identifier_from_cmdline,"fixed"))			dz->process = EQ;
	else if(!strcmp(prog_identifier_from_cmdline,"lohi"))			dz->process = LPHP;
	else if(!strcmp(prog_identifier_from_cmdline,"variable"))		dz->process = FSTATVAR;
	else if(!strcmp(prog_identifier_from_cmdline,"bank"))			dz->process = FLTBANKN;
	else if(!strcmp(prog_identifier_from_cmdline,"bankfrqs"))		dz->process = FLTBANKC;
	else if(!strcmp(prog_identifier_from_cmdline,"userbank"))		dz->process = FLTBANKU;
	else if(!strcmp(prog_identifier_from_cmdline,"varibank"))		dz->process = FLTBANKV;
	else if(!strcmp(prog_identifier_from_cmdline,"varibank2"))		dz->process = FLTBANKV2;
	else if(!strcmp(prog_identifier_from_cmdline,"iterated"))		dz->process = FLTITER;
	else if(!strcmp(prog_identifier_from_cmdline,"sweeping"))		dz->process = FLTSWEEP;
	else if(!strcmp(prog_identifier_from_cmdline,"phasing"))		dz->process = ALLPASS;
	else if(!strcmp(prog_identifier_from_cmdline,"vfilters"))		dz->process = MAKE_VFILT;
	else {
		sprintf(errstr,"Unknown program identification string '%s'\n",prog_identifier_from_cmdline);
		return(USAGE_ONLY);
	}
	return FINISHED;
}

/********************************************************************************************/
/********************************** FORMERLY IN usage.c *************************************/
/********************************************************************************************/

/******************************** USAGE1 ********************************/

int usage1(void)
{
	sprintf(errstr,
	"USAGE: filter NAME (mode) infile outfile (datafile) parameters\n"
	"\n"
	"where NAME can be any one of\n"
	"\n"
	"fixed      lohi         variable\n"
	"bank       bankfrqs     userbank      varibank      varibank2\n"
	"iterated   sweeping     phasing	   vfilters\n"
	"\n"
	"Type 'filter  userbank'  for more info on filter userbank... ETC.\n");
	return(USAGE_ONLY);
}

/******************************** USAGE2 ********************************/

int usage2(char *str)
{
	if (!strcmp(str,"fixed")) {		 	/* EQ */
		fprintf(stdout,
		"CUT OR BOOST, ABOVE, BELOW OR AROUND A GIVEN FRQ\n\n"
		"USAGE: filter fixed 1-2  infile outfile        boost/cut freq [-ttail] [-sprescale]\n"
		"OR:    filter fixed 3    infile outfile bwidth boost/cut freq [-ttail] [-sprescale]\n\n"
		"MODES ARE...\n"
		"1) BOOST OR CUT BELOW GIVEN FRQ\n"
		"2) BOOST OR CUT ABOVE GIVEN FRQ\n"
		"3) BOOST OR CUT A BAND CENTERED ON GIVEN FRQ\n\n"
		"BWIDTH     is the filter bandwidth in Hz for Mode 3 only\n"
		"BOOST/CUT  is the boost or cut, in dB\n"
		"FREQ       is the filter frequency in Hz\n"
		"TAIL       decay tail duration\n"
		"PRESCALE   scales the INPUT to the filter.\n");

	} else if (!strcmp(str,"lohi")) {		 /* LOHI */
		fprintf(stdout,
    	"FIXED LOW PASS OR HIGH PASS FILTER.\n\n"
    	"USAGE:\nfilter lohi mode infile outfile attenuation pass-band stop-band [-ttail] [-sprescale]\n\n"
	    "MODES ARE\n"
	    "1) Pass-band and stop-band as freq in Hz.\n"
	    "2) Pass-band and stop-band as (possibly fractional) midi notes.\n\n"
	    "ATTENUATION Gain reduction of filter, in dB. Range(0 to %.0lf)\n"
	    "            Greater attenuation, sharper filter, but longer to calculate.\n"
	    "PASS-BAND   last pitch to be passed by the filter.\n"
	    "STOP-BAND   first pitch to be stopped by the filter.\n"
	    "            If stop-band is above pass-band, this is a lopass filter.\n"
	    "            If stop-band is below pass-band, this is a hipass filter.\n"
		"TAIL       decay tail duration\n"
	    "-s          Prescale input: Avoid overflows: Range(%.3lf to %.1lf)\n",
	    MIN_DB_ON_16_BIT,FLT_MINEQPRESCALE,FLT_MAXEQPRESCALE);

	} else if (!strcmp(str,"variable")) {		 /* FSTATVAR */
		fprintf(stdout,
		"LOPASS, HIGH-PASS, BANDPASS, OR NOTCH FILTER WITH VARIABLE FRQ\n\n"
		"USAGE: filter variable mode infile outfile acuity gain frq [-ttail]\n\n"
		"MODES ARE..\n"
		"1) NOTCH (Band Reject)\n\n"
		"2) BAND-PASS\n"
		"3) LOW-PASS\n"
		"4) HIGH-PASS\n"
	    "ACUITY   acuity of filter: Range(%lf to %.1lf).\n"
	    "         Smaller vals give tighter filter.\n"
		"GAIN     overall gain on output:  Range(%lf to %.1lf)\n"
		"         Rule of thumb:\n"
		"         if acuity = (1/3)to-power-n: gain = (2/3)-to-power-n\n"
	    "FRQ      frq of filter:           Range(%.1lf to srate/2)\n\n"	
		"ACUITY and FRQ can vary over time.\n"
		"TAIL       decay tail duration\n",MIN_ACUITY,MAX_ACUITY,FLT_MINGAIN,FLT_MAXGAIN,FLT_MINFRQ);

	} else if (!strcmp(str,"bank")) {		 /* FLTBANKN */
     	fprintf(stdout,
		"BANK OF FILTERS, WITH TIME-VARIABLE Q\n\n"
		"USAGE: filter bank 1-3 infile outfile Q gain lof hif       [-ttail] [-sscat] [-d]\n"
		"OR:    filter bank 4-6 infile outfile Q gain lof hif param [-ttail] [-sscat] [-d]\n\n"
		"MODES...\n"
		"1)  HARMONIC SERIES over lofrq.\n"
		"2)  ALTERNATE HARMONICS over lofrq.\n"
		"3)  SUBHARMONIC SERIES below hifrq.\n"
		"4)  HARMONIC SERIES WITH LINEAR OFFSET: param = offset in Hz.\n"
		"5)  EQUAL INTERVALS BETWEEN lo & hifrq: param = no. of filters.\n"
		"6)  EQUAL INTERVALS BETWEEN lo & hifrq: param = interval semitone-size.\n\n"
		"Q      Q (tightness) of filters (Range %lf <= Q < %.1lf)\n"
		"GAIN   overall gain (Range %lf to %.1lf).\n"
		"LOF    lofrq limit of filters  (Range: %.0lf to srate/3).\n"
		"HIF    hifrq limit of filters  (Range: lofrq+ to srate/3).\n"
		"TAIL   decay tail duration\n"
		"SCAT   Random scatter of filter frqs (Range 0 to 1: Default 0).\n"
		"-d     Double filtering.\n\n"
		"Q may vary over time.\n",MINQ,MAXQ,FLT_MINGAIN,FLT_MAXGAIN,FLT_MINFRQ);

	} else if (!strcmp(str,"bankfrqs")) {		 /* FLTBANKC */
     	fprintf(stdout,
		"GENERATE A LIST OF FREQUENCIES FOR USE AS A FILTERBANK\n"
		"(ADD AMPLITUDES TO THE TEXTFILE FOR USE WITH 'USERBANK')\n\n"
		"USAGE: filter bankfrqs 1-3 anysndfile outtextfile lof hif\n"
		"OR:    filter bankfrqs 4-6 anysndfile outtextfile lof hif param\n\n"
		"MODES...\n"					   
		"1)  HARMONIC SERIES over lofrq.\n"
		"2)  ALTERNATE HARMONICS over lofrq.\n"
		"3)  SUBHARMONIC SERIES below hifrq.\n"
		"4)  HARMONIC SERIES WITH LINEAR OFFSET: param = offset in Hz.\n"
		"5)  EQUAL INTERVALS BETWEEN lo & hifrq: param = no. of filters.\n"
		"6)  EQUAL INTERVALS BETWEEN lo & hifrq: param = interval semitone-size.\n\n"
		"LOF    lofrq limit of filters  (Range: %.0lf to srate/3).\n"
		"HIF    hifrq limit of filters  (Range: lofrq+ to srate/3).\n"
//		"SCAT   Random scatter of filter frqs (Range 0 to 1: Default 0).\n\n" /* RWD Apr 2011 this param not supported here */
		"NB sampling rate of input soundfile determines frq range\n",FLT_MINFRQ);

	} else if (!strcmp(str,"userbank")) {		 /* FLTBANKU */
      	fprintf(stdout,
	   	"USER-DEFINED FILTERBANK,WITH TIME-VARIABLE Q\n\n"
		"USAGE: filter userbank mode infile outfile datafile Q gain [-ttail] [-d]\n\n"
		"MODES are\n"
		"1)  Filter-pitches as frq in Hz.\n"
		"2)  Filter-pitches as MIDI values.\n\n"
		"DATAFILE: has Pitch & Amp of filters (paired, one pair on each line).\n"
		"          Ranges: Pitch (%.0lfHz to sr/3) Amp (%lf to %.1lf)\n"
		"          Amplitude may also be expressed in decibels.\n"
		"          Comment-lines (starting with ';') may be used.\n\n"
		"Q:        Q (tightness) of filter: Range(%lf to %.1lf)\n"
		"GAIN      overall gain, Range(%lf to %.1lf).\n"
		"TAIL      decay tail duration\n"
		"-d        double filtering.\n\n"
		"Q may vary over time.",FLT_MINFRQ,FLT_MINGAIN,FLT_MAXGAIN,MINQ,MAXQ,FLT_MINGAIN,FLT_MAXGAIN);

	} else if (!strcmp(str,"varibank")) {		 /* FLTBANKV */
		fprintf(stdout,
	   	"USER-DEFINED TIME_VARYING FILTERBANK,WITH TIME-VARIABLE Q\n\n"
		"USAGE:\nfilter varibank mode infile outfile data Q gain [-ttail] [-hhcnt] [-rrolloff] [-d]\n\n"
		"MODES ARE...\n"
		"1)  Enter filter-pitches as frq, in Hz.\n"
		"2)  Enter filter-pitches as MIDI values.\n\n"
		"DATAFILE: has lines of data for filter bands at successive times.\n"
		"          Each line contains the following items\n"
		"                Time:   Pitch1 Amp1   [Pitch2 Amp2    etc....].\n"
		"          Pitch and Amp values must be paired:\n"
		"          any number of pairs can be used in a line,\n"
		"          BUT each line must have SAME number of pairs on it.\n"
		"          (To eliminate a band in any line(s), set its amplitude to 0.0).\n"
		"          Time values (in secs) must be in ascending order (and >=0.0)\n"
		"          Pitch vals may be EITHER frq, OR MIDI, depending on MODE.\n"
		"          Amp values may be numeric, or dB values (e.g. -4.1dB).\n"
		"          Comment-lines may be used: start these with ';'.\n\n"
		"Q         Q (tightness) of filter : Range(%lf to %.1lf).\n"		
		"GAIN:     overall gain: Range: (%lf to %.1lf)\n"					
		"TAIL      decay tail duration\n"
		"HCNT      No of harmonics of each pitch to use: Default 1.\n"
		"          High harmonics of high pitches may be beyond nyquist.\n"
		"          (No-of-pitches times no-of-harmonics determines program speed).\n"
		"ROLLOFF   Level drop (in dB) from one harmonic to next. Range(0 to %.1lf)\n"
		"-d        double filtering.\n\n"
		"Q may also vary over time.\n",MINQ,MAXQ,FLT_MINGAIN, FLT_MAXGAIN,MIN_DB_ON_16_BIT);

	} else if (!strcmp(str,"varibank2")) {		 /* FLTBANKV */
		fprintf(stdout,
	   	"USER-DEFINED TIME_VARYING FILTERBANK,WITH TIME-VARIABLE Q AND PARTIALS\n"
		"USAGE:\nfilter varibank2 mode infil outfil data Q gain [-ttail] [-d]\n"
		"MODES ARE...\n"
		"1)  Enter filter-pitches as frq, in Hz.\n"
		"2)  Enter filter-pitches as MIDI values.\n\n"
		"DATAFILE: has lines of data for filter bands at successive times.\n"
		"          as varibank filter....\n"
		"          Followed by a line with a '#' sign at the start.\n"
		"          Followed by 2nd set of lines, similar to the pitchdata lines\n"
		"          but now the data is a time followed by any number of pairs of\n"
		"          partial number (possibly fractional) + amplitude of partial.\n"
		"          Times for pitch AND for partials data MUST START AT ZERO.\n"
		"Q         Q (tightness) of filter : Range(%lf to %.1lf).\n"		
		"GAIN:     overall gain: Range: (%lf to %.1lf)\n"					
		"TAIL      decay tail duration\n"
		"-d        double filtering.\n\n"
		"Q may also vary over time.\n",MINQ,MAXQ,FLT_MINGAIN,FLT_MAXGAIN);

	} else if (!strcmp(str,"iterated")) {		 /* FLTITER */
		fprintf(stdout,
    	"ITERATE SOUND, WITH CUMULATIVE FILTERING BY A FILTERBANK.\n\n"
    	"USAGE: filter iterated mode infile outfile datafile Q gain delay dur \n"
    	"         [-sprescale] [-rrand] [-ppshift] [-aashift] [-d] [-i] [-e] [-n]\n\n"
		"MODES ARE...\n"
		"1)  Enter filter-pitches as frq, in Hz.\n"
		"2)  Enter filter-pitches as MIDI values.\n\n"
    	"DATAFILE  has Pitch & Amp of filters (paired, one pair on each line).\n"
    	"          Pitch, as Hz or MIDI, Range(%.0lfHz to srate/3)\n"		
    	"          Amp, Range(%lf to %.1lf), can also be entered as dB vals\n\n" 
    	"Q         Q (tightness) of filter: Range(%lf to %.1lf)\n"    
    	"GAIN      overrall gain in filtering process (%lf < gain < %.1lf)\n"
    	"DELAY     (average) delay between iterations (secs).\n"
    	"          Range(>0 to %.0lf)\n"
    	"DUR       (min) duration of output file (>infile duration).\n"
    	"PRESCALE  gain on the INPUT to the filtering process.\n"
    	"          Range (0.0 - 1.0) Default 1.0\n"
    	"          If set to 0.0, Program automatically divides input level by\n"
    	"          the max no. of sound overlays occuring in the iteration process.\n"
    	"RAND      delaytime-randomisation: Range 0 (none) to 1 (max).\n"
    	"PSHIFT    max pitchshift of any segment in (fractions of) semitones (>=0).\n"
    	"ASHIFT    max amplitude reduction of any segment: Range (0.0 - 1.0)\n"
    	"-d        double filtering.\n"
	  	"-i        Turn off Interpolation during pitch shift: (fast but dirty).\n"
    	"-e        Exponential decay added: each seg gets quieter before next enters.\n"
    	"-n        turns OFF normalisation: segs may grow or fall in level quickly.\n",
    	FLT_MINFRQ,FLT_MINGAIN,FLT_MAXGAIN,MINQ, MAXQ, FLT_MINGAIN,FLT_MAXGAIN,FLT_MAXDELAY);

	} else if (!strcmp(str,"sweeping")) {		 /* FLTSWEEP */
		fprintf(stdout,
	    "FILTER WHOSE FOCUS-FREQUENCY SWEEPS OVER A RANGE OF FREQUENCIES\n\n"
		"USAGE:\n"
		"filter sweeping mode infile outfile acuity gain lofrq hifrq sweepfrq [-ttail] [-pphase]\n\n"
		"MODES ARE...\n"
		"1) NOTCH (Band-reject). 3) LOW-PASS\n"
		"2) BAND-PASS            4) HIGH-PASS  \n\n"
	    "ACUITY   Acuity of filter:  Range(%lf to %.1lf)\n"		
	    "         Smaller vals give tighter filter.\n"
		"GAIN     overall gain on output:   Range(%lf to %.1lf)\n"		
		"         Rule of thumb:\n"
		"         if acuity = (1/3)to-power-n: gain = (2/3)-to-power-n\n"
	    "LOFRQ    lowest frq to sweep to:   Range(%.1lf to srate/2)\n"	
	    "HIFRQ    highest frq to sweep to:  Range(%.1lf to srate/2)\n"	
	    "SWEEPFRQ frq of the sweep itself:  Range(0.0 to %.1lf)\n"		
	    "         e.g. to sweep once over range, set sweepfrq to infiledur/2 (default)\n"		
		"         and set phase to 0 (upsweep) or .5(downsweep)\n"
		"TAIL      decay tail duration\n"
		"PHASE    start of sweep: Range(0-1)\n"
		"         vals are: 0 = (lofrq) rising to .5 = (hifrq) falling to 1 = (lofrq).\n"
		"         Default value is .25 (midfrq,rising)\n\n"
		"ACUITY, LOFRQ, HIFRQ and SWEEPFRQ may all vary over time.\n"
		"This filter is most effective in BAND-PASS or LOW-PASS mode, at low ACUITY.\n",
		MIN_ACUITY,MAX_ACUITY,FLT_MINGAIN,FLT_MAXGAIN,FLT_MINFRQ,FLT_MINFRQ,FLT_MAXSWEEP);

	} else if (!strcmp(str,"phasing")) {		 /* ALLPASS */
		fprintf(stdout,
		"PHASESHIFT SOUND, OR PRODUCE 'PHASING' EFFECT\n\n"
		"USAGE: filter phasing mode infile outfile gain delay [-ttail] [-sprescale] [-l]\n\n"
		"MODES ARE..\n"
		"1) ALLPASS FILTER (PHASE-SHIFTED)\n"
		"2) PHASING EFFECT\n\n"
		"GAIN      Range (-1.0 to 1.0)\n"
		"          In MODE 2, phasing effect increases as gain increases from -1\n"
		"          BUT a gain of 1.0 will produce complete phase cancellation\n"
		"          and the output signal will be zero.\n"
		"DELAY     >0.0 MS\n"
		"TAIL      decay tail duration\n"
		"PRESCALE  gain on the INPUT to the filtering process.\n"
    	"          Range (0.0 - 1.0) Default 1.0\n"
		"-l        linear interpolation of changing delay vals (default: logarithmic)\n\n"
		"DELAY may vary over time.\n");
	} else if (!strcmp(str,"vfilters")) {		 /* MAKE VFILTER FIXED-PITCH DATA FILES */
		fprintf(stdout,
		"MAKE FIXED-PITCH DATA-FILES FOR VARIBANK FILTER\n\n"
		"USAGE: filter vfilters inpitchfile outfile\n\n"
		"INPITCHFILE     Textfile, contains a list of MIDI or FRQ pitch values\n"
		"                with one or more values on each line.\n"
		"Each line is converted to a data file for a fixed pitch(es) filter,\n"
		"of the 'varibank' type.\n"
		"Producing outfile0.txt, outfile1.txt etc.\n");
	} else
		fprintf(stdout,"Unknown option '%s'\n",str);
	return(USAGE_ONLY);
}

/******************************** USAGE3 ********************************/

int usage3(char *str1,char *str2)
{
	sprintf(errstr,"Insufficient parameters on command line.\n");
	return(USAGE_ONLY);
}

/******************************** INNER_LOOP (redundant)  ********************************/

int inner_loop
(int *peakscore,int *descnt,int *in_start_portion,int *least,int *pitchcnt,int windows_in_buf,dataptr dz)
{
	return(FINISHED);
}

/**************************** CHECK_SEQ_AND_RANGE_OF_FILTER_DATA2 *******************************/

int check_seq_and_range_of_filter_data2(double *fbrk,double *hbrk,dataptr dz)
{
	double lasttime = -1.0;
	double maxpartial = dz->infile->srate/(2.0 * FLT_MINFRQ);
	double minpartial = 1.0;
	int n, m, lineno = 0;
	double frqmax  = dz->infile->srate/2.0;
	double midimax = unchecked_hztomidi(frqmax);
	double midimin = unchecked_hztomidi(FLT_MINFRQ);

	int wordcnt_in_line = dz->iparam[FLT_ENTRYCNT];
	for(n=0;n<dz->iparam[FLT_WORDCNT];n++) {
		m = n%wordcnt_in_line;
		if(m==0) {
			lineno++;				/* original line-no : ignoring comments */
			if(fbrk[n] <= lasttime) {
				sprintf(errstr,"Time is out of sequence in pitch data line %d\n",lineno);
				return(DATA_ERROR);
			}
			lasttime = fbrk[n];
		} else if(ODD(m)) {
			if(dz->mode==FLT_MIDI) {
				if(fbrk[n] < midimin || fbrk[n] > midimax) {
					sprintf(errstr,"midi value [%.3lf] out of range (%.1f - %.1f) on line %d\n",
					fbrk[n],midimin,midimax,lineno);
					return(DATA_ERROR);
				}
				fbrk[n] = miditohz(fbrk[n]);
			} else {
				if(fbrk[n] < FLT_MINFRQ || fbrk[n] > frqmax) {
					sprintf(errstr,"frq_or_midi value [%.3lf] out of range (%.1f - %.1f) on line %d\n",
					fbrk[n],FLT_MINFRQ,frqmax,lineno);
					return(DATA_ERROR);
				}
			}
		} else
			fbrk[n] = max(fbrk[n],MINFILTAMP);	/* Zero filter amp, forced to +ve, but effectively zero */
	}
	wordcnt_in_line = dz->iparam[HRM_ENTRYCNT];
	lasttime = -1.0;
	lineno = 0;
	for(n=0;n<dz->iparam[HRM_WORDCNT];n++) {
		m = n%wordcnt_in_line;
		if(m==0) {
			lineno++;
			if(hbrk[n] <= lasttime) {
				sprintf(errstr,"Time is out of sequence in partials data line %d\n",lineno);
				return(DATA_ERROR);
			}
			lasttime = hbrk[n];
		} else if(ODD(m)) {
			if(hbrk[n]<minpartial|| hbrk[n]>maxpartial) {
				sprintf(errstr,"partial value [%.3lf] out of range (%lf - %.1f) on line %d\n",hbrk[n],minpartial,maxpartial,lineno);
				return(DATA_ERROR);
			}
		} else
			hbrk[n] = max(hbrk[n],MINFILTAMP);	/* Zero filter amp, forced to +ve, but effectively zero */
	}
	return(FINISHED);
}

/**************************** READ_DATA_FROM_T_AND_P_VARY_INFILE *******************************/

int read_data_from_t_and_p_vary_infile(char *filename,dataptr dz)
{
	int exit_status;
	char *temp, *p, *thisword;
	int maxlinelen, frqcnt = 0, hcnt;
	int total_wordcnt = 0, number_of_rows = 0, total_number_of_rows = 0,columns_in_this_row,columns_in_row = 0;
	int  harmdata = 0, ishash = 0, thisarray;

	double val;
	if((dz->fp = fopen(filename,"r"))==NULL) {
		sprintf(errstr,"Cannot open datafile %s\n",filename);
		return(DATA_ERROR);
	}
	if((exit_status = getmaxlinelen2(&maxlinelen,dz->fp))<0)
		return(exit_status);
	if((fseek(dz->fp,0,0))<0) {
		sprintf(errstr,"Seek failed in get_data_from_t_and_p_vary_infile()\n");
		return(SYSTEM_ERROR);
	}
	if((temp = (char *)malloc((maxlinelen+2) * sizeof(char)))==NULL) {
		sprintf(errstr,"INSUFFICIENT MEMORY for temporary line storage.\n");
		return(MEMORY_ERROR);
	}
	while(fgets(temp,maxlinelen,dz->fp)!=NULL) {
		columns_in_this_row = 0;
		if(is_an_empty_line_or_a_comment(temp))
			continue;
		p = temp;
		while(get_word_from_string(&p,&thisword)) {
			if(!strncmp(thisword,"#",1)) {
				if(harmdata) {
					sprintf(errstr,"HASH SIGN AT WRONG PLACE IN DATA in LINE %d.\n",total_number_of_rows+1);
					return(DATA_ERROR);
				}
				dz->iparam[FLT_WORDCNT] = total_wordcnt;
				dz->iparam[FLT_ENTRYCNT] = columns_in_row;
				total_wordcnt = 0;
				number_of_rows = 0;
				frqcnt = columns_in_row - 1;
				harmdata = 1;
				ishash = 1;
				break;
			}
			if((exit_status = get_level(thisword,&val))<0) {	/* reads vals */
				free(temp);
				return(exit_status);
			}
			if(harmdata)
				thisarray = FLT_HBRK;
			else
				thisarray = FLT_FBRK;
			if(total_wordcnt==0) {
				if ((dz->parray[thisarray] = (double *)malloc(sizeof(double)))==NULL) {
					sprintf(errstr,"INSUFFICIENT MEMORY to reallocate filter brktables.\n");
					return(MEMORY_ERROR);
				}
				dz->parray[thisarray][total_wordcnt] = val;
			} else {
				if ((dz->parray[thisarray] = (double *)realloc(dz->parray[thisarray],(total_wordcnt+1) * sizeof(double)))==NULL) {
					sprintf(errstr,"INSUFFICIENT MEMORY to reallocate filter brktables.\n");
					return(MEMORY_ERROR);
				}
			}
			dz->parray[thisarray][total_wordcnt] = val;
			columns_in_this_row++;
			total_wordcnt++;
		}
		if(ishash) {
			ishash = 0;
			continue;
		}
		if(number_of_rows==0) {

			if((columns_in_row = columns_in_this_row)<3) {
				sprintf(errstr,"Insufficient data in row 1 of file %s.\n",filename);
				free(temp);
				return(DATA_ERROR);
			} else if (ODD(columns_in_row - 1)) {
				sprintf(errstr,"Value and Amp data not paired correctly (or no Time) in row %d of file %s.\n",total_number_of_rows+1,filename);
				free(temp);
				return(DATA_ERROR);
			}
		} else if(columns_in_this_row!=columns_in_row) {
			free(temp);
			if(columns_in_this_row < columns_in_row)
				sprintf(errstr,"Not enough entries in row %d of file %s\n",total_number_of_rows+1,filename);
			else
				sprintf(errstr,"Too many entries in row %d of file %s\n",total_number_of_rows+1,filename);
			return(DATA_ERROR);
		}
		number_of_rows++;
		total_number_of_rows++;
	}
	dz->iparam[HRM_WORDCNT] = total_wordcnt;
	free(temp);
	if(columns_in_row<3) {
		sprintf(errstr,"Insufficient data in each row, to define filters.\n");
		return(DATA_ERROR);
	}
	dz->iparam[HRM_ENTRYCNT] = columns_in_row;
	hcnt = columns_in_row - 1;
	dz->iparam[FLT_CNT]       = frqcnt/2;
	dz->iparam[FLT_HARMCNT]   = hcnt/2;
	dz->iparam[FLT_CNT] *= dz->iparam[FLT_HARMCNT];
	if(fclose(dz->fp)<0) {
		fprintf(stdout,"WARNING: Failed to close input textfile %s.\n",filename);
		fflush(stdout);
	}
	if(dz->parray[FLT_FBRK][0] !=0.0) {
		sprintf(errstr,"FIRST TIME VALUE FOR PITCHES MUST BE ZERO\n");
		return(DATA_ERROR);
	}
	if(dz->parray[FLT_HBRK][0] !=0.0) {
		sprintf(errstr,"FIRST TIME VALUE FOR HARMONICS MUST BE ZERO\n");
		return(DATA_ERROR);
	}
	return check_seq_and_range_of_filter_data2(dz->parray[FLT_FBRK],dz->parray[FLT_HBRK],dz);
}

/**************************** GETMAXLINELEN2 *******************************/

int getmaxlinelen2(int *maxcnt,FILE *fp)
{
	int thiscnt = 0;
	int gothash = 0;
	int gotdata1 = 0, gotdata2 = 0;
	char c;
	*maxcnt = 0;

	while((c= (char)fgetc(fp))!=EOF) {
		if(!gothash) {
			if(c=='#') {
				if(!gotdata1) {
					sprintf(errstr,"FAILED TO GET FIRST SET OF DATA (PITCH INFORMATION) BEFORE HASH SIGN\n");
					return(DATA_ERROR);
				}
				while((c= (char)fgetc(fp))!=EOF) {
					if(c == '\n' || c == ENDOFSTR) {
						gothash = 1;
						thiscnt = 0;
						break;
					}
				}
				if(!gothash)
					break;
			} else {
				if(c=='\n' || c == ENDOFSTR) {
					*maxcnt = max(*maxcnt,thiscnt);
					thiscnt = 0;
					gotdata1 = 1;
				} else {
					thiscnt++;			
				}
			}
		} else {
			if(c=='\n' || c == ENDOFSTR) {
				*maxcnt = max(*maxcnt,thiscnt);
				thiscnt = 0;
				gotdata2 = 1;
			} else {
				thiscnt++;			
			}
		}
	}
	if(!gotdata2) {
		sprintf(errstr,"FAILED TO GET SECOND SET OF DATA (PARTIALS INFORMATION) AFTER HASH SIGN\n");
		return(DATA_ERROR);
	}
	*maxcnt = (int)max(*maxcnt,thiscnt);
	*maxcnt += 4;	/* NEWLINE, ENDOFSTR and safety!! */
	return(FINISHED);
}			

