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



/* floatsam version */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

#include <structures.h>
#include <cdpmain.h>
#include <tkglobals.h>
#include <pnames.h>
#include <mix.h>
#include <processno.h>
#include <modeno.h>
#include <globcon.h>
#include <logic.h>
#include <filetype.h>

#include <mixxcon.h>
#include <flags.h>
#include <speccon.h>
#include <arrays.h>
#include <special.h>
#include <formants.h>
#include <sfsys.h>
#include <osbind.h>

#include <srates.h>
#include <mix1.h>

//TW UPDATE
#include <ctype.h>

#ifdef unix
#define round(x) lround((x))
#endif

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

#define MAX_SYNCATT_CHANS	(STEREO)

static int  create_mix_buffers(dataptr dz);
static int  create_syncatt_buffer(dataptr dz);
static int  create_mixtwo_buffer(dataptr dz);
//TW UPDATE
static int create_mixmany_buffer(dataptr dz);

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

static int  mixtwo_sndprops_consistency(dataptr dz);
static int  get_filesearch_data(dataptr dz);
static int  syncatt_presets(dataptr dz);
static int  check_syncatt_window_factor(dataptr dz);
static int  mixtwarp_consistency(dataptr dz);
static int  mixswarp_consistency(dataptr dz);
static int  mix_consistency(dataptr dz);
static int  mix_atten(dataptr dz);
static int  adjust_levels(int lineno,int total_words,dataptr dz);

//TW UPDATES
static int cyc_preprop(dataptr dz);
static int create_cycinbi_buffer(dataptr dz);
static int invalid_data_items(char *str);
static int read_mixgrid_file(char *str,dataptr dz);
static int read_and_scale_balance_data(char *filename,dataptr dz);
static int addtomix_consistency(dataptr dz);
static int mixmodel_consistency(dataptr dz);
static int read_inbetween_ratios(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(MIX):
	case(MIXTEST):
	case(MIXMAX):
	case(MIXFORMAT):
	case(MIXDUMMY):
	case(MIXTWARP):
	case(MIXSWARP):	   	   
//TW UPDATE
	case(MIX_PAN):	   	   
	case(MIXGAIN):	   	   
	case(MIXSHUFL):	   	   
	case(MIXSYNC):
//TW UPDATES
	case(MIX_ON_GRID):
	case(AUTOMIX):
	case(ADDTOMIX):
	case(MIX_MODEL):
	case(MIX_AT_STEP):	   	   
								dz->extra_bufcnt = 0;	dz->bufcnt = 0;		break;
//TW UPDATE
	case(MIXMANY):				dz->extra_bufcnt = 0;	dz->bufcnt = 3;		break;
	case(MIXSYNCATT):			dz->extra_bufcnt = 0;	dz->bufcnt = 1;		break;
	case(MIXTWO):
	case(MIXBALANCE):			dz->extra_bufcnt = 0;	dz->bufcnt = 4;		break;
	case(MIXCROSS):				dz->extra_bufcnt = 0;	dz->bufcnt = 2;		break;
	case(MIXINTERL):			dz->extra_bufcnt = 0;	dz->bufcnt = 1 + MAX_MI_OUTCHANS;	break;
	case(CYCINBETWEEN):
	case(MIXINBETWEEN):			dz->extra_bufcnt = 0;	dz->bufcnt = 3;		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(MIX):
	case(MIXTWO):
	case(MIXBALANCE):
	case(MIXTEST):
	case(MIXMAX):
	case(MIXFORMAT):
	case(MIXDUMMY):
	case(MIXINTERL):
	case(MIXMANY):
	case(MIXINBETWEEN):
	case(ADDTOMIX):
	case(MIX_MODEL):
	case(MIX_AT_STEP):	   	   
					dz->array_cnt = 0; dz->iarray_cnt = 0; dz->larray_cnt = 0; dz->ptr_cnt = 0;	dz->fptr_cnt = 0; break;
	case(CYCINBETWEEN):
					dz->array_cnt = 0; dz->iarray_cnt = 0; dz->larray_cnt = 4; dz->ptr_cnt = 0;	dz->fptr_cnt = 0; break;

	case(MIX_ON_GRID):
	case(AUTOMIX):
	case(MIXSWARP):	   	   
	case(MIX_PAN):	   	   
					dz->array_cnt = 1; dz->iarray_cnt = 0; dz->larray_cnt = 0; dz->ptr_cnt = 0;	dz->fptr_cnt = 0; break;
	case(MIXGAIN):	   	   
	case(MIXTWARP):
	case(MIXSHUFL):	   	   
					dz->array_cnt = 2; dz->iarray_cnt = 0; dz->larray_cnt = 0; dz->ptr_cnt = 0;	dz->fptr_cnt = 0; break;

	case(MIXSYNC):	dz->array_cnt = 1; dz->iarray_cnt = 1; dz->larray_cnt = 0; dz->ptr_cnt = 0;	dz->fptr_cnt = 0; break;
	case(MIXSYNCATT): dz->array_cnt=3; dz->iarray_cnt = 2; dz->larray_cnt = 3; dz->ptr_cnt = 0;	dz->fptr_cnt = 0; break;
	case(MIXCROSS):	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;
	}
	/* RWD all now floats */
	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);
		}
		/* RWD 4:2002 lparray shadowed by lfarray for submix syncatt */

		if((dz->lfarray = (float    **)malloc(dz->larray_cnt * sizeof(float *)))==NULL) {
			sprintf(errstr,"INSUFFICIENT MEMORY for internal float arrays.\n");
			return(MEMORY_ERROR);
		}		
		for(n=0;n<dz->larray_cnt;n++){
			dz->lparray[n] = NULL;
			dz->lfarray[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)   {  	  
//TW FIXED
		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(MIX):	           setup_process_logic(MIXFILES_ONLY,	 	UNEQUAL_SNDFILE,	SNDFILE_OUT,	dz);	break;
	case(MIXTWO):
	case(MIXBALANCE):	   setup_process_logic(TWO_SNDFILES,	 	UNEQUAL_SNDFILE,	SNDFILE_OUT,	dz);	break;
	case(MIXCROSS):		   setup_process_logic(TWO_SNDFILES,	 	UNEQUAL_SNDFILE,	SNDFILE_OUT,	dz);	break;
	case(MIXINTERL):	   setup_process_logic(MANY_SNDFILES,	 	UNEQUAL_SNDFILE,	SNDFILE_OUT,	dz);	break;
	case(CYCINBETWEEN):
	case(MIXINBETWEEN):	   setup_process_logic(TWO_SNDFILES,	 	OTHER_PROCESS,		NO_OUTPUTFILE,	dz);	break;
	case(MIXTEST):		   setup_process_logic(MIXFILES_ONLY,	 	SCREEN_MESSAGE,		NO_OUTPUTFILE,	dz);	break;
	case(MIXFORMAT):	   setup_process_logic(NO_FILE_AT_ALL,	 	SCREEN_MESSAGE,		NO_OUTPUTFILE,	dz);	break;
	case(MIXDUMMY):	   	   setup_process_logic(MANY_SNDFILES,	 	TO_TEXTFILE,		TEXTFILE_OUT,	dz);	break;
//TW UPDATES
	case(ADDTOMIX):	   setup_process_logic(ANY_NUMBER_OF_ANY_FILES, TO_TEXTFILE,		TEXTFILE_OUT,	dz);	break;
	case(MIX_MODEL):   setup_process_logic(ANY_NUMBER_OF_ANY_FILES, TO_TEXTFILE,		TEXTFILE_OUT,	dz);	break;
	case(MIXMANY):	   	   setup_process_logic(MANY_SNDFILES,	 	UNEQUAL_SNDFILE,	SNDFILE_OUT,	dz);	break;
	case(MIX_ON_GRID):	   setup_process_logic(MANY_SNDFILES,	 	TO_TEXTFILE,		TEXTFILE_OUT,	dz);	break;
	case(AUTOMIX):	       setup_process_logic(MANY_SNDFILES,	 	UNEQUAL_SNDFILE,	SNDFILE_OUT,	dz);	break;

	case(MIXSYNC):	   	   setup_process_logic(SND_OR_MIXLIST_ONLY,	TO_TEXTFILE,		TEXTFILE_OUT,	dz);	break;
	case(MIXSYNCATT): setup_process_logic(SND_SYNC_OR_MIXLIST_ONLY, TO_TEXTFILE,		TEXTFILE_OUT,	dz);	break;
	case(MIXTWARP):	   	   setup_process_logic(MIXFILES_ONLY,	 	TO_TEXTFILE,		TEXTFILE_OUT,	dz);	break;
	case(MIXSWARP):	   	   setup_process_logic(MIXFILES_ONLY,	 	TO_TEXTFILE,		TEXTFILE_OUT,	dz);	break;
//TW UPDATES
	case(MIX_PAN):	   	   setup_process_logic(MIXFILES_ONLY,	 	TO_TEXTFILE,		TEXTFILE_OUT,	dz);	break;
	case(MIX_AT_STEP):	   setup_process_logic(MANY_SNDFILES,	 	TO_TEXTFILE,		TEXTFILE_OUT,	dz);	break;

	case(MIXGAIN):	   	   setup_process_logic(MIXFILES_ONLY,	 	TO_TEXTFILE,		TEXTFILE_OUT,	dz);	break;
	case(MIXSHUFL):	   	   setup_process_logic(MIXFILES_ONLY,	 	TO_TEXTFILE,		TEXTFILE_OUT,	dz);	break;
	case(MIXMAX):
		switch(dz->mode) {
		case(MIX_LEVEL_ONLY):	  setup_process_logic(MIXFILES_ONLY,SCREEN_MESSAGE,		NO_OUTPUTFILE,	dz);	break;
		case(MIX_CLIPS_ONLY):	  setup_process_logic(MIXFILES_ONLY,TO_TEXTFILE,		TEXTFILE_OUT,	dz);	break;
		case(MIX_LEVEL_AND_CLIPS):setup_process_logic(MIXFILES_ONLY,TO_TEXTFILE,		TEXTFILE_OUT,	dz);	break;
		default:
			sprintf(errstr,"Unknown mode for MIXMAX in assign_process_logic()\n");
			return(PROGRAM_ERROR);
		}
		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) {
	case(MIXSHUFL):
		if(dz->mode==MSH_DUPL_AND_RENAME)
			dz->has_otherfile = TRUE;
		else 
			dz->has_otherfile = FALSE;
		break;
	case(MIXBALANCE):
	case(MIXTWO):	  		/* normal file comparison ignores stereo-mono conflict */
	case(MIXCROSS):			/* But most of these progs need consistent channel-count in input snds. */
	case(MIXINTERL):		/* Sending header data  to 'otherfile' */
	case(MIXINBETWEEN):		/* allows comparison to be made in a distinct function. */
	case(CYCINBETWEEN):
	case(AUTOMIX):
		dz->has_otherfile = TRUE;
		break;
	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(MIX):			exit_status = set_internalparam_data("iii",ap);				break;
	case(MIXTWO):		exit_status = set_internalparam_data("dd",ap);				break;
	case(MIXBALANCE):	exit_status = set_internalparam_data("",ap);				break;
	case(MIXCROSS):	    
		switch(mode) {
		case(MCLIN):	exit_status = set_internalparam_data("0iid",ap);				break;
		case(MCCOS):	exit_status = set_internalparam_data( "iid",ap);				break;	
		}
		break;
	case(MIXINTERL):    exit_status = set_internalparam_data("",ap);					break;
	case(CYCINBETWEEN):
	case(MIXINBETWEEN): exit_status = set_internalparam_data("",ap);					break;
	case(MIXTEST):		exit_status = set_internalparam_data("",ap);					break;
	case(MIXMAX):		exit_status = set_internalparam_data("iii",ap);				break;
	case(MIXFORMAT):	exit_status = set_internalparam_data("",ap);					break;
	case(MIXMANY):	
	case(MIX_ON_GRID):	
	case(AUTOMIX):	
	case(ADDTOMIX):	
	case(MIX_MODEL):
	case(MIX_AT_STEP):	

	case(MIXDUMMY):		exit_status = set_internalparam_data("",ap);					break;
	case(MIXSYNC):		exit_status = set_internalparam_data("",ap);					break;
	case(MIXSYNCATT):	exit_status = set_internalparam_data("i",ap);					break;
	case(MIXTWARP):		exit_status = set_internalparam_data("",ap);					break;
	case(MIXSWARP):		exit_status = set_internalparam_data("",ap);					break;
//TW UPDATE
	case(MIX_PAN):		exit_status = set_internalparam_data("",ap);					break;
	case(MIXGAIN):		exit_status = set_internalparam_data("",ap);					break;
	case(MIXSHUFL):		exit_status = set_internalparam_data("",ap);					break;
	default:
		sprintf(errstr,"Unknown process in set_legal_internalparam_structure()\n");
		return(PROGRAM_ERROR);
	}
	return(exit_status);		
}

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

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

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

	switch(ap->special_data) {
	case(SNDFILENAME):				return read_new_filename(str,dz);
//TW UPDATES
	case(GRIDDED_MIX):				return read_mixgrid_file(str,dz);
	case(AUTO_MIX):					return read_and_scale_balance_data(str,dz);
	case(INBTWN_RATIOS):
		if(dz->mode == INBI_RATIO)
			return read_inbetween_ratios(str,dz);
		break;
	default:
		sprintf(errstr,"Unknown special_data type: read_special_data()\n");
		return(PROGRAM_ERROR);
	}
	return(FINISHED);	/* NOTREACHED */
}

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

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

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

	switch(dz->process) {
	case(MIX):	
	case(MIXMAX):		return mix_preprocess(dz);

	case(MIXTWO):		
		if((exit_status = create_sized_outfile(dz->outfilename,dz))<0)
				return(exit_status);
		if((exit_status = mixtwo_preprocess(dz))<0)
			return(exit_status);
		return FINISHED;
	case(MIXCROSS):		return mixcross_preprocess(dz);
	case(MIXINBETWEEN):	return get_inbetween_ratios(dz);
	case(CYCINBETWEEN):	return cyc_preprop(dz);
	case(MIXTEST):			case(MIXFORMAT):		case(MIXDUMMY):
	case(MIXSYNC):			case(MIXSYNCATT):
	case(MIXMANY):			case(MIX_ON_GRID):		case(AUTOMIX):	
	case(ADDTOMIX):			case(MIX_AT_STEP):		case(MIX_MODEL):	

		return(FINISHED);
	case(MIXBALANCE):
		if((dz->infile->channels > 2 ||  dz->otherfile->channels > 2) && (dz->otherfile->channels != dz->infile->channels)) {
			sprintf(errstr,"With multichannel files, both files must have same number of channels.\n");
			return(DATA_ERROR);
		}
		break;
	case(MIXINTERL):
		if(dz->infilecnt > MAX_MI_OUTCHANS) {
			sprintf(errstr,"Too many infiles for interpolation program. Max %d\n",MAX_MI_OUTCHANS);
			return(USER_ERROR);
		}

		/* RWD 4:2002  now we can open outfile with corect params! */
		dz->infile->channels = dz->infilecnt;	/* ARRGH! */
		if((exit_status = create_sized_outfile(dz->outfilename,dz))<0)
				return(exit_status);


		return(FINISHED);
	case(MIXTWARP):
		initrand48();
		switch(dz->mode) {
		case(MTW_TIMESORT):							break;
		default:	dz->iparam[MSH_STARTLINE]--;	break;
		}
		return(FINISHED);
	case(MIXSWARP):
		initrand48();
		switch(dz->mode) {
		case(MSW_TWISTALL):	break;
		case(MSW_TWISTONE): dz->iparam[MSW_TWLINE]--;		break;
		default:			dz->iparam[MSH_STARTLINE]--;	break;
		}
		return(FINISHED);
	case(MIXGAIN):
		dz->iparam[MSH_STARTLINE]--;
		return(FINISHED);
	case(MIXSHUFL):
		initrand48();
		if((exit_status = check_new_filename(dz->wordstor[dz->extra_word],dz))<0)
			return(exit_status);
		dz->iparam[MSH_STARTLINE]--;
		return(FINISHED);
//TW UPDATE
	case(MIX_PAN):
		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 */
{	
	double normalisation;

	switch(dz->process) {
	case(MIX):			case(MIXCROSS):
	case(MIXINTERL):	case(MIXINBETWEEN):	case(MIXMAX):	case(CYCINBETWEEN):
		display_virtual_time(0L,dz);
		break;
	}
	switch(dz->process) {
	case(MIX):		   	return mmix(dz);
	case(MIXTWO):
		display_virtual_time(0L,dz);
	   	return mixtwo(dz);
	case(MIXBALANCE):	return cross_stitch(dz);
	case(MIXCROSS):	   	return mix_cross(dz);
	case(MIXINTERL):   	return mix_interl(dz);
	case(CYCINBETWEEN):
	case(MIXINBETWEEN):	return mix_inbetween(dz);
	case(MIXMAX):	  	return mix_level_check(&normalisation,dz);
	case(MIXTEST):	  	return mix_syntax_check(dz);

	case(MIXFORMAT):  	
		if(!sloom)
			return usage2("fileformat");
		return (FINISHED);	
//TW UPDATES
	case(MIXMANY):
	   	return mixmany(dz);
	case(AUTOMIX):		return do_automix(dz);
	case(ADDTOMIX):		return addtomix(dz);
	case(MIX_MODEL):	return mix_model(dz);
	case(MIX_ON_GRID):	
	case(MIX_AT_STEP):	

	case(MIXDUMMY):   	return create_mixdummy(dz);

	case(MIXSYNC):	  	return mix_sync(dz);
	case(MIXSYNCATT): 	return synchronise_mix_attack(dz);

	case(MIXTWARP):	  	return mix_timewarp(dz);
	case(MIXSWARP):	  	return mix_spacewarp(dz);
//TW UPDATE
	case(MIX_PAN):	  	return panmix(dz);
	case(MIXGAIN):	  	return mix_gain(dz);
	case(MIXSHUFL):	  	return mix_shufl(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(MIX):
		if(!(flteq(dz->param[MIX_ATTEN],1.0))) {
			if((exit_status = mix_atten(dz)) <0)
				return(exit_status);
		}
		/* fall thro */
	case(MIXMAX):		return set_up_mix(dz);

	case(MIXTWO):
	case(MIXBALANCE):
	case(MIXCROSS):
	case(MIXINTERL):
	case(MIXINBETWEEN):	return mixtwo_sndprops_consistency(dz);

	case(MIXSYNCATT):	return syncatt_presets(dz);
	case(MIXTWARP):		return mixtwarp_consistency(dz);
	case(MIXSWARP):		return mixswarp_consistency(dz);

	case(MIXGAIN):
	case(MIXSHUFL):		return mix_consistency(dz);
	case(MIX_MODEL):	return mixmodel_consistency(dz);
	case(ADDTOMIX):		return addtomix_consistency(dz);
	case(AUTOMIX):		/* bufer pointers established once infilecnt is known */
		dz->bufcnt = dz->infilecnt + 1;
		return establish_groucho_bufptrs_and_extra_buffers(dz);
	}
	return(FINISHED);
}

/******************************** MIXTWO_SNDPROPS_CONSISTENCY ********************************/
/*RWD comment: for a large source file this is a heavy burden; we need inline buffer conversion rather than
 a temporary file */
/* RWD: this is mono/stereo only - where is that tested for? */
int mixtwo_sndprops_consistency(dataptr dz)
{
	double infiledur2, infiledur, sr = (double)dz->infile->srate; 

	int n, m, k, exit_status;
	int tempfd;
	float *tempbuf;
	int samps_read, samps_written;	
//TW just to make it clearer
	int shsecsize = F_SECSIZE;
//	int dbl_shsecsize = shsecsize * 2;
	int outsize_stereo;

	if(dz->infile->srate!=dz->otherfile->srate) {
		sprintf(errstr,"Different sample-rates in input files: can't proceed.\n");
		return(USER_ERROR);
	}
	switch(dz->process) {
	case(MIXTWO): 		
		if(dz->infile->channels > 2) {
			if(dz->infile->channels != dz->otherfile->channels) {
				sprintf(errstr,"With multichannel files, both files must have same number of channels.\n");
				return(DATA_ERROR);
			}
			dz->outchans = dz->infile->channels;
		} else  {
			if(dz->infile->channels!=dz->otherfile->channels) {
				fprintf(stdout,"INFO: Files have different channel count. Converting to stereo.\n");
				fflush(stdout);
				if((tempbuf = (float *)malloc(/*SECSIZE*/shsecsize * 2 * sizeof(float)))==NULL) {
					sprintf(errstr,"Cannot create temporary buffer for mono to stereo conversion.\n");
					return(MEMORY_ERROR);
				}
				if(dz->infile->channels == 1) {
					is_converted_to_stereo = 0;
					outsize_stereo = dz->insams[0] * 2;
				} else {
					is_converted_to_stereo = 1;
					outsize_stereo = dz->insams[1] * 2;
				}
				if(sloom) {
					if((tempfd = sndcreat_formatted("#temp",outsize_stereo,dz->infile->stype,
							STEREO,dz->infile->srate,CDP_CREATE_NORMAL)) < 0) {
						is_converted_to_stereo = -1;
						sprintf(errstr,"Cannot open output file '#temp'\n");
						return(DATA_ERROR);
					}
				} else if((tempfd = sndcreat_formatted("_temp",outsize_stereo,dz->infile->stype,
						STEREO,dz->infile->srate,CDP_CREATE_NORMAL)) < 0) {
					is_converted_to_stereo = -1;
					sprintf(errstr,"Cannot open output file '_temp': %s\n",sferrstr());
					return(DATA_ERROR);
				}
				while((samps_read  = fgetfbufEx(tempbuf, shsecsize,dz->ifd[is_converted_to_stereo],0))>0) {
					int d_samps_read = samps_read * 2;
					for(m = d_samps_read - 2,k = d_samps_read - 1, 
							n = samps_read - 1;
								n >=0; n--,m-=2,k-=2) {
						tempbuf[m] = tempbuf[n];						
						tempbuf[k] = tempbuf[n];						
					}
					if((samps_written = fputfbufEx(tempbuf,/*SECSIZE*/samps_read * 2,tempfd))<0) {
						sprintf(errstr,"Can't write to output temporary soundfile: (is hard-disk full?).\n");
						return(SYSTEM_ERROR);
					}
					if(samps_written != samps_read * 2) {
						sprintf(errstr,"Error in data accounting while converting mono file to stereo.\n");
						return(SYSTEM_ERROR);
					}
				}
				dz->infile->channels = 2;

				if(sndcloseEx(tempfd) < 0) {
					sprintf(errstr,"Failed to close temporary stereo soundfile.\n");
					return(SYSTEM_ERROR);
				}
				if(sndcloseEx(dz->ifd[is_converted_to_stereo]) < 0) {
					sprintf(errstr,"Failed to close input soundfile, at conversion to stereo.\n");
					return(SYSTEM_ERROR);
				}
				dz->ifd[is_converted_to_stereo] = -1;
				if(sloom) {
					if((dz->ifd[is_converted_to_stereo] = sndopenEx("#temp",0,CDP_OPEN_RDONLY)) < 0) {
						sprintf(errstr,"Failed to reopen temporary file '#temp' for output.\n");
						return(SYSTEM_ERROR);
					}
				} else {
					if((dz->ifd[is_converted_to_stereo] = sndopenEx("_temp",0,CDP_OPEN_RDONLY)) < 0) {
						sprintf(errstr,"Failed to reopen temporary file '#temp' for output.\n");
						return(SYSTEM_ERROR);
					}
				}
				sndseekEx(dz->ifd[is_converted_to_stereo],0,0);
				dz->insams[is_converted_to_stereo] *= 2;
			}
		}
		if((exit_status = reset_peak_finder(dz))<0)
			return(exit_status);
		break;
	case(MIXBALANCE):
		break;
	default:	
		if(dz->infile->channels!=dz->otherfile->channels) {
			sprintf(errstr,"Different no. of channels in input files: can't proceed.\n");
			return(USER_ERROR);
		}
		break;
	}
	if(dz->otherfile->filetype!=SNDFILE) {
		sprintf(errstr,"2nd file is not a soundfile: can't proceed.\n");
		return(USER_ERROR);
	}
	infiledur  =(double)(dz->insams[0]/dz->infile->channels)/sr; 
	infiledur2 =(double)(dz->insams[1]/dz->infile->channels)/sr; 

	switch(dz->process) {
	case(MIXTWO):		
		if(dz->param[MIX_SKIP] >= infiledur2) {
			sprintf(errstr,"SKIP INTO 2ND FILE exceeds length of that file: cannot proceed\n");
			return(DATA_ERROR);
		}		
		break;
	case(MIXBALANCE):
		break;
	case(MIXCROSS):
		if(dz->param[MCR_BEGIN] >= infiledur2 + dz->param[MCR_STAGGER]) {
			sprintf(errstr,"CROSSFADE START is after end of 2nd sndfile: cannot proceed\n");
			return(DATA_ERROR);
		}
		if(dz->param[MCR_END] > infiledur + infiledur2) {
			sprintf(errstr,"CROSSFADE END is beyond end of sndfiles: cannot proceed\n");
			return(DATA_ERROR);
		}
		break;
	}
	return(FINISHED);
}

/************************* GET_FILESEARCH_DATA ************************/

int get_filesearch_data(dataptr dz)
{   
	int    exit_status;
	int   srate = 0, total_words, n;
	int    *inchans;
	/* RWD: sets a scan window only; crude, but we keep it for now */
//TW better to use global
	int   shsecsize = F_SECSIZE, *samplen;
	double filedur;
	double minsyncscan;
	double *start, *end;
	int    textfile_filetype = dz->infile->filetype;

	if(dz->linecnt > SF_MAXFILES) {
		sprintf(errstr,"Maximum number of sndfiles [%d] exceeded.\n",SF_MAXFILES);
		return(USER_ERROR);
	} else if(dz->linecnt <= 0) {
		sprintf(errstr,"No lines found in sync data file.\n");
		return(USER_ERROR);
	}
	if((dz->parray[MSY_STARTSRCH] = (double *)malloc(dz->linecnt * sizeof(double)))==NULL) {
		sprintf(errstr,"INSUFFICIENT MEMORY for sync startsearch array.\n");
		return(MEMORY_ERROR);
	}
	start = dz->parray[MSY_STARTSRCH];
	if((dz->parray[MSY_ENDSRCH] = (double *)malloc(dz->linecnt * sizeof(double)))==NULL) {
		sprintf(errstr,"INSUFFICIENT MEMORY for sync endsearch array.\n");
		return(MEMORY_ERROR);
	}
	end = dz->parray[MSY_ENDSRCH];
	if((dz->lparray[MSY_SAMPSIZE] = (int *)malloc(dz->linecnt * sizeof(int)))==NULL) {
		sprintf(errstr,"INSUFFICIENT MEMORY for sync sampsizes array.\n");
		return(MEMORY_ERROR);
	}
	samplen = dz->lparray[MSY_SAMPSIZE];
	if((dz->iparray[MSY_CHANS] = (int *)malloc(dz->linecnt * sizeof(int)))==NULL) {
		sprintf(errstr,"INSUFFICIENT MEMORY for sync channels array.\n");
		return(MEMORY_ERROR);
	}
	inchans = dz->iparray[MSY_CHANS];
	if((dz->lparray[MSY_PEAKSAMP] = (int *)malloc(dz->linecnt * sizeof(int)))==NULL) {
		sprintf(errstr,"INSUFFICIENT MEMORY for sync peaks array.\n");
		return(MEMORY_ERROR);
	}
	if((exit_status = establish_file_data_storage_for_mix((int)1,dz))<0)
		return(exit_status);
	total_words = 0;
	for(n=0;n<dz->linecnt;n++) {
		if((exit_status = open_file_and_retrieve_props(n,dz->wordstor[total_words],&srate,dz))<0)
			return(exit_status);
		samplen[n]	= dz->insams[0];
		inchans[n]	= dz->infile->channels;
		total_words += dz->wordcnt[n];
	}

	if(srate > SAMPLE_RATE_DIVIDE)  dz->iparam[MSY_SRFAC]=2;
	else							dz->iparam[MSY_SRFAC]=1;

	minsyncscan = (double)((shsecsize * dz->iparam[MSY_SRFAC])/MAX_WINFAC)/(double)srate;

	total_words = 0;
	for(n=0;n<dz->linecnt;n++) {
		switch(dz->wordcnt[n]) {
		case(3):
			if(dz->infile->filetype==MIXFILE) {
				sprintf(errstr,"Anomalous line length [%d] in mixfile\n",dz->wordcnt[n]);
				return(PROGRAM_ERROR);
			}
			if(sscanf(dz->wordstor[total_words+1],"%lf",&(start[n]))!=1) {
				sprintf(errstr,"Failed to read starttime: line %d: get_filesearch_data()\n",n+1);
				return(PROGRAM_ERROR);
			}
			if(sscanf(dz->wordstor[total_words+2],"%lf",&(end[n]))!=1) {
				sprintf(errstr,"Failed to read endtime: line %d: get_filesearch_data()\n",n+1);
				return(PROGRAM_ERROR);
			}
			if((start[n] < 0.0) || (end[n] < 0.0) || (start[n] + minsyncscan >= end[n])) {
				sprintf(errstr,"Impossible or incompatible searchtimes [%.5lf to %.5lf]: line %d.\n",
				start[n],end[n],n+1);
				return(USER_ERROR);
			}
// TW MOVED July 2004
			filedur = (double)(samplen[n]/inchans[n])/(double)srate;
			if(start[n] >= filedur - minsyncscan) {
				sprintf(errstr,"starttime on line %d is beyond effective file end.\n",n+1);
				return(DATA_ERROR);
			}
			if(end[n] >= filedur)
				end[n] = -1.0;	/* flags END_OF_SNDFILE */
			break;
		default:
			start[n] = 0.0;
			end[n]   = -1.0;	/* flags END_OF_SNDFILE */
			break;
		}
		total_words += dz->wordcnt[n];
	}
	dz->infile->filetype = textfile_filetype;
	return(FINISHED);
}

/************************* OPEN_FILE_AND_RETRIEVE_PROPS *******************************/

int open_file_and_retrieve_props(int filecnt,char *filename,int *srate,dataptr dz)
{
	int exit_status;
	if((exit_status = open_file_retrieve_props_open(filecnt,filename,srate,dz))<0)
		return(exit_status);
	if(sndcloseEx(dz->ifd[0])<0) {
		sprintf(errstr, "Failed to close input file %s: line %d: open_file_and_retrieve_props()\n",filename,filecnt+1);
		return(SYSTEM_ERROR);
	}
	dz->ifd[0] = -1;
	return(FINISHED);
}

/************************* OPEN_FILE_RETRIEVE_PROPS_OPEN *******************************/

int open_file_retrieve_props_open(int filecnt,char *filename,int *srate,dataptr dz)
{
	int exit_status;
	double maxamp, maxloc;
	int maxrep;
	int getmax = 0, getmaxinfo = 0;
	infileptr ifp;
	if((ifp = (infileptr)malloc(sizeof(struct filedata)))==NULL) {
		sprintf(errstr,"INSUFFICIENT MEMORY to store data on file %s\n",filename);
		return(MEMORY_ERROR);
	}
	/* OK to use sndopenEx? */
	if((dz->ifd[0] = sndopenEx(filename,0,CDP_OPEN_RDONLY)) < 0) {
		sprintf(errstr,"Failed to open sndfile %s: line %d: open_file_retrieve_props_open()\n",filename,filecnt+1);
		return(SYSTEM_ERROR);
	}
	/* this use revised version that recognizes floatsam sndfile */
	if((exit_status = readhead(ifp,dz->ifd[0],filename,&maxamp,&maxloc,&maxrep,getmax,getmaxinfo))<0)
		return(exit_status);
	copy_to_fileptr(ifp,dz->infile);
	if(dz->infile->filetype!=SNDFILE) {
		sprintf(errstr,"%s is not a soundfile: line %d: open_file_retrieve_props_open()\n",filename,filecnt+1);
		return(PROGRAM_ERROR);
	}
	if(filecnt==0)
		*srate = dz->infile->srate;
	else if(dz->infile->srate != *srate) {
		sprintf(errstr,"incompatible srate: file %s: line %d: open_file_retrieve_props_open()\n",filename,filecnt+1);
		return(DATA_ERROR);
	}
	if((dz->insams[0] = sndsizeEx(dz->ifd[0]))<0) {	    			
		sprintf(errstr, "Can't read size of input file %s: line %d: open_file_retrieve_props_open()\n",filename,filecnt+1);
		return(PROGRAM_ERROR);
	}
	if(dz->insams[0] <=0) {
		sprintf(errstr, "Zero size for input file %s: line %d: open_file_retrieve_props_open()\n",filename,filecnt+1);
		return(DATA_ERROR);
	}			
	return(FINISHED);
}

/***************************** SYNCATT_PRESETS **************************/

int syncatt_presets(dataptr dz)
{
	int exit_status;
	if(!check_syncatt_window_factor(dz))
			return(USER_ERROR);
	if((exit_status= get_filesearch_data(dz))<0)
		return(exit_status);
	return(FINISHED);
}

/***************************** CHECK_SYNCATT_WINDOW_FACTOR **************************/

int check_syncatt_window_factor(dataptr dz)
{
	int valid_value = MIN_WINFAC;
	while(valid_value <= MAX_WINFAC) {
		if(dz->iparam[MSY_WFAC]==valid_value)
			return(TRUE);
		valid_value *= 2;
	}
	return(FALSE);
}

/****************************** MIXTWARP_CONSISTENCY *********************************/

int mixtwarp_consistency(dataptr dz)
{
	switch(dz->mode) {
	case(MTW_TIMESORT):	
		break;
	default:
		if(dz->iparam[MSH_ENDLINE] < dz->iparam[MSH_STARTLINE]) {
			sprintf(errstr,"Start and endline values incompatible.\n");
			return(USER_ERROR);
		}
	}
	return(FINISHED);
}

/****************************** MIXSWARP_CONSISTENCY *********************************/

int mixswarp_consistency(dataptr dz)
{
	if(dz->mode!=MSW_TWISTALL && dz->mode!=MSW_TWISTONE) {
		if(dz->iparam[MSH_ENDLINE] < dz->iparam[MSH_STARTLINE]) {
			sprintf(errstr,"Start and endline values incompatible.\n");
			return(USER_ERROR);
		}
	}
	return(FINISHED);
}

/****************************** MIX_CONSISTENCY *********************************/

int mix_consistency(dataptr dz)
{
	if(dz->iparam[MSH_ENDLINE] < dz->iparam[MSH_STARTLINE]) {
		sprintf(errstr,"Start and endline values incompatible.\n");
		return(USER_ERROR);
	}
	return(FINISHED);
}

//TW UPDATE: NEW FUNCTION
/****************************** ADDTOMIX_CONSISTENCY *********************************/

int addtomix_consistency(dataptr dz)
{
	if(dz->infile->filetype != MIXFILE) {
		sprintf(errstr,"The FIRST FILE must be a MIXFILE.\n");
		return(USER_ERROR);
	}
	return(FINISHED);
}

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

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

int allocate_large_buffers(dataptr dz)
{
	switch(dz->process) {
	case(MIX):			case(MIXMAX):			
		return create_mix_buffers(dz);

	case(MIXTWO):
	case(MIXBALANCE):
		return create_mixtwo_buffer(dz);

	case(MIXINTERL):
		dz->bufcnt = 1 + dz->infilecnt;
		return create_sndbufs(dz);
	case(MIXCROSS):		case(MIXINBETWEEN):		
	case(AUTOMIX):
		return create_sndbufs(dz);
	case(CYCINBETWEEN):			
		return create_cycinbi_buffer(dz);
	case(MIXMANY):			
		return create_mixmany_buffer(dz);

	case(MIXSYNCATT):		
		return create_syncatt_buffer(dz);

	case(MIXFORMAT):	case(MIXDUMMY):		case(MIXTEST):
	case(MIXTWARP):		case(MIXSWARP):		case(MIXGAIN):
	case(MIXSHUFL):		case(MIXSYNC):		case(MIX_MODEL):
	case(MIX_ON_GRID):	case(ADDTOMIX):		case(MIX_PAN):
	case(MIX_AT_STEP):	

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

/*************************** CREATE_MIX_BUFFERS **************************
 *
 * (1)	Basic buffersize needs an extra factor of 2 because MONO_TO_STEREO conversion
 *		reads 1/2 a buffer into dz->sampbuf[STEREOBUF], and that 1/2 a buf needs to be
 *		a multiple of F_SECSIZE samples!!
 * (2)	As well as the input buffers, need an output buffer, 
 		and a stereo-buffer for panned-stereo.
 * (3)	Initialise space left in outbuffer to all of it.
 */

int create_mix_buffers(dataptr dz)
{
	int exit_status;
	size_t bigbufsize;
	size_t bloksize = SECSIZE * 2;  			/* 1 */
	/*RWD NB longsize_in_floats was longsize_in_shorts...*/
//TW MIXMAX CALCULATIONS ARE DONE IN FLOATS, SO BUFFER-OF-LONGS NOT NEEDED
	switch(dz->process) {
	case(MIX):
		dz->bufcnt += 2;					/* allow for panned_stereo_buffer and output buffer */
		break;
	case(MIXMAX):
//TW AMPLITUDE SUM NOW IN FLOATS
		dz->bufcnt += 2;					
		break;
	default:
		sprintf(errstr,"Unknown case: create_mix_buffers()\n");
		return(PROGRAM_ERROR);
	}
	bloksize = dz->bufcnt * bloksize;
	bigbufsize = (size_t)Malloc(-1);
	if((bigbufsize = (bigbufsize/bloksize) * bloksize) <= 0)
		bigbufsize = bloksize;
	if((dz->bigbuf = (float *)Malloc(bigbufsize)) == NULL) {
		sprintf(errstr, "INSUFFICIENT MEMORY to create sound buffers.\n");
		return(MEMORY_ERROR);
	}

	dz->buflen =(int)( bigbufsize/sizeof(float)/dz->bufcnt);	 		/* length of floats buffers */
//TW NO ADJUSTMENTS NEEDED FOR BUFFER-0F-LONGS
	if((exit_status = establish_groucho_bufptrs_and_extra_buffers(dz))<0)
		return(exit_status);
	dz->bufcnt -= 2;							 /* bufcnt is now a count of input buffers only */
	dz->sampbuf[OBUFMIX] = dz->bigbuf;
//TW NO ADJUSTMENTS NEEDED FOR BUFFER-0F-LONGS
	dz->sampbuf[STEREOBUF] = dz->bigbuf + dz->buflen;

	dz->sampbuf[IBUFMIX] = dz->sampbuf[STEREOBUF] + dz->buflen;
	return(FINISHED);
}

/*************************** CREATE_SYNCATT_BUFFER **************************/

int create_syncatt_buffer(dataptr dz)
{
	size_t bigbufsize;
	long n, bufactor = dz->iparam[MSY_SRFAC] * MAX_SYNCATT_CHANS;
	size_t fsecbytesize = F_SECSIZE * sizeof(float);
	bigbufsize = (size_t)Malloc(-1);
//TW SAFER
	if((bigbufsize  = (bigbufsize/(fsecbytesize*bufactor)) * (fsecbytesize*bufactor))<=0) {
		bigbufsize  = fsecbytesize*bufactor;
	}
	dz->buflen = (int)(bigbufsize/sizeof(float));
	if((dz->bigbuf = (float *)malloc((size_t)(dz->buflen * sizeof(float)))) == NULL) {
		sprintf(errstr,"INSUFFICIENT MEMORY to create sound buffers.\n");
		return(MEMORY_ERROR);
	}
	for(n=0;n<dz->bufcnt;n++)
		dz->sbufptr[n] = dz->sampbuf[n] = dz->bigbuf + (dz->buflen * n);
	dz->sampbuf[n] = dz->bigbuf + (dz->buflen * n);
	return(FINISHED);
}

/*************************** CREATE_MIXTWO_BUFFER **************************/

int create_mixtwo_buffer(dataptr dz)
{
	size_t bigbufsize;
	int n, bufactor;
	int samptodouble = sizeof(double)/sizeof(float);
	size_t fsecbytesize = F_SECSIZE * sizeof(float);
	bigbufsize = (size_t) Malloc(-1);
	if(dz->process == MIXBALANCE) {
// MULTICHAN 2009 --> 
		if(dz->infile->channels > 2)
			dz->bufcnt = dz->infile->channels * 2;
// <-- MULTICHAN 2009
		if((bigbufsize  /= dz->bufcnt) <= 0) {
			sprintf(errstr,"INSUFFICIENT MEMORY for sound buffers.\n");
			return(MEMORY_ERROR);
		}
		if((bigbufsize = ((bigbufsize/fsecbytesize * dz->bufcnt) * fsecbytesize * dz->bufcnt)) <= 0) {
			bigbufsize = fsecbytesize * dz->bufcnt;
		}
		if((dz->bigbuf = (float *)malloc((size_t)(bigbufsize * dz->bufcnt))) == NULL) {
			sprintf(errstr,"INSUFFICIENT MEMORY to create sound buffers.\n");
			return(MEMORY_ERROR);

		}
		dz->buflen = (int)(bigbufsize/sizeof(float));
		dz->sampbuf[0] = dz->bigbuf;
// MULTICHAN 2009 --> 
		if(dz->infile->channels > 2)
			dz->sampbuf[1] = dz->sampbuf[0] + (dz->buflen * dz->infile->channels);
// <-- MULTICHAN 2009
		else if(dz->infile->channels == 1)
			dz->sampbuf[1] = dz->sampbuf[0] + dz->buflen;
		else
			dz->sampbuf[1] = dz->sampbuf[0] + (dz->buflen * 2);
	} else {
// MULTICHAN 2009 --> 
		if(dz->infile->channels > 2) {
			dz->bufcnt = 3 + samptodouble;
			bufactor  = dz->bufcnt * dz->infile->channels;
// <-- MULTICHAN 2009
		} else
			bufactor = (dz->bufcnt - 1) + samptodouble;
		if((bigbufsize  = (bigbufsize/(fsecbytesize*bufactor)) * (fsecbytesize*bufactor))<=0)
			bigbufsize  = fsecbytesize*bufactor;
		if((dz->bigbuf = (float *) malloc(bigbufsize) ) == NULL) {
			sprintf(errstr,"INSUFFICIENT MEMORY to create sound buffers.\n");
			return(MEMORY_ERROR);
		}
// MULTICHAN 2009 --> 
		if(dz->infile->channels > 2)
			bufactor = dz->bufcnt;
// <-- MULTICHAN 2009
		bigbufsize /= bufactor;
		dz->buflen = (int)(bigbufsize/sizeof(float));
		for(n=0;n<dz->bufcnt;n++)
			dz->sbufptr[n] = dz->sampbuf[n] = dz->bigbuf + (dz->buflen * n);
		dz->sampbuf[n] = dz->bigbuf + (dz->buflen * bufactor);
	}
	return(FINISHED);
}

// TW NEW FUNCTION: ADJUSTED FOR floats
/*************************** CREATE_MIXMANY_BUFFER **************************/

int create_mixmany_buffer(dataptr dz)
{
	size_t n, bufactor, bigbufsize;
	size_t samptodouble = sizeof(double)/sizeof(float);
	size_t fsecbytesize = F_SECSIZE * sizeof(float);
	bigbufsize = (long)Malloc(-1);
	bufactor = dz->bufcnt + samptodouble;
	if((bigbufsize  = (bigbufsize/(fsecbytesize*bufactor)) * (fsecbytesize*bufactor))<=0) {
		bigbufsize  = fsecbytesize*bufactor;
	}
	if((dz->bigbuf = (float *)malloc(bigbufsize)) == NULL) {
		sprintf(errstr,"INSUFFICIENT MEMORY to create sound buffers.\n");
		return(MEMORY_ERROR);
	}
	bigbufsize /= bufactor;
	dz->buflen = (int)(bigbufsize/sizeof(float));
	for(n=0;n<dz->bufcnt;n++)
		dz->sbufptr[n] = dz->sampbuf[n] = dz->bigbuf + (dz->buflen * n);
	dz->sampbuf[n] = dz->bigbuf + (dz->buflen * bufactor);
	return(FINISHED);
}

/*************************** CREATE_CYCINBI_BUFFER **************************/

int create_cycinbi_buffer(dataptr dz)
{
	int n;
	/*RWD April 2004 */ 
	int insize_0,insize_1, bigbufsize;
	insize_0 = dz->insams[0] * sizeof(float);
	insize_1 = dz->insams[1] * sizeof(float);
 /*	int bigbufsize = max(dz->infilesize[0], dz->infilesize[1]) + F_SECSIZE; */
	bigbufsize = max(insize_0, insize_1) + F_SECSIZE; 
	bigbufsize = (bigbufsize/F_SECSIZE) * F_SECSIZE * dz->bufcnt;
	if((dz->bigbuf = (float *)malloc((size_t)(bigbufsize))) == NULL) {
		sprintf(errstr,"INSUFFICIENT MEMORY to create sound buffers.\n");
		return(MEMORY_ERROR);
	}
	bigbufsize /= dz->bufcnt;
	dz->buflen = bigbufsize/sizeof(float);
	for(n=0;n<dz->bufcnt;n++)
		dz->sbufptr[n] = dz->sampbuf[n] = dz->bigbuf + (dz->buflen * n);
	dz->sampbuf[n] = dz->bigbuf + (dz->buflen * dz->bufcnt);
	return(FINISHED);
}

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

int get_process_no(char *prog_identifier_from_cmdline,dataptr dz)
{
	if     (!strcmp(prog_identifier_from_cmdline,"mix"))			dz->process = MIX;
	else if(!strcmp(prog_identifier_from_cmdline,"test"))			dz->process = MIXTEST;
	else if(!strcmp(prog_identifier_from_cmdline,"getlevel"))		dz->process = MIXMAX;
	else if(!strcmp(prog_identifier_from_cmdline,"fileformat"))		dz->process = MIXFORMAT;
	else if(!strcmp(prog_identifier_from_cmdline,"dummy"))			dz->process = MIXDUMMY;
	else if(!strcmp(prog_identifier_from_cmdline,"timewarp"))		dz->process = MIXTWARP;
	else if(!strcmp(prog_identifier_from_cmdline,"spacewarp"))		dz->process = MIXSWARP;
	else if(!strcmp(prog_identifier_from_cmdline,"attenuate"))		dz->process = MIXGAIN;
	else if(!strcmp(prog_identifier_from_cmdline,"shuffle"))		dz->process = MIXSHUFL;
	else if(!strcmp(prog_identifier_from_cmdline,"sync"))			dz->process = MIXSYNC;
	else if(!strcmp(prog_identifier_from_cmdline,"syncattack"))		dz->process = MIXSYNCATT;
	else if(!strcmp(prog_identifier_from_cmdline,"merge"))			dz->process = MIXTWO;
	else if(!strcmp(prog_identifier_from_cmdline,"balance"))		dz->process = MIXBALANCE;
	else if(!strcmp(prog_identifier_from_cmdline,"crossfade"))		dz->process = MIXCROSS;
	else if(!strcmp(prog_identifier_from_cmdline,"interleave"))		dz->process = MIXINTERL;
	else if(!strcmp(prog_identifier_from_cmdline,"inbetween"))		dz->process = MIXINBETWEEN;
//TW UPDATES
	else if(!strcmp(prog_identifier_from_cmdline,"mergemany"))		dz->process = MIXMANY;
	else if(!strcmp(prog_identifier_from_cmdline,"ongrid"))			dz->process = MIX_ON_GRID;
	else if(!strcmp(prog_identifier_from_cmdline,"atstep"))			dz->process = MIX_AT_STEP;
	else if(!strcmp(prog_identifier_from_cmdline,"faders"))			dz->process = AUTOMIX;
	else if(!strcmp(prog_identifier_from_cmdline,"addtomix"))		dz->process = ADDTOMIX;
	else if(!strcmp(prog_identifier_from_cmdline,"pan"))			dz->process = MIX_PAN;
	else if(!strcmp(prog_identifier_from_cmdline,"model"))			dz->process = MIX_MODEL;
	else if(!strcmp(prog_identifier_from_cmdline,"inbetween2"))		dz->process = CYCINBETWEEN;
	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: submix NAME (mode) infiles     outfile(s) [parameters]:\n"
	"OR:    submix NAME (mode) mixdatafile outfile    [parameters]:\n"
	"\n"
	"where NAME can be any one of\n"
	"\n"
	"merge      balance     crossfade   interleave   inbetween   inbetween2\n"
	"mix        getlevel      attenuate\n"
	"shuffle    timewarp      spacewarp      sync       syncattack\n"
	"test       fileformat    dummy     addtomix     atstep\n"
	"ongrid     faders      mergemany   pan          model\n"
	"\n"
	"Type 'submix  mix'  for more info on submix  mix.\n"
	"Type 'submix  fileformat'  for more info on mixfile fileformat... ETC\n");
	return(USAGE_ONLY);
}

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

int usage2(char *str)
{
	if(!strcmp(str,"mix")) {		
	    sprintf(errstr,
	    "MIX SOUNDS AS INSTRUCTED IN A MIXFILE\n\n"
    	"USAGE:	submix mix mixfile outsndfile [-sSTART] [-eEND] [-gATTENUATION] [-a]\n\n"
    	"START       gives starttime START (to start mixing later than zero).\n"
    	"END         gives output endtime END (to stop mix before its true end).\n"
    	"ATTENUATION reduces level of entire mix (range >0-1).\n\n"
		"Note that the START and END params are intended for mix TESTING purposes only.\n"
		"If you want to keep output from such a testmix, you should TOPNTAIL it.\n\n"
    	"-a    alternative mix algorithm, slightly slower,\n"
    	"      but may avoid clipping in special circumstances.\n");
	} else if(!strcmp(str,"test")) {		
	    sprintf(errstr,
	    "TEST THE SYNTAX OF A MIXFILE\n\n"
    	"USAGE:	submix test mixfile\n\n");
	} else if(!strcmp(str,"getlevel")) {		
	    sprintf(errstr,
	    "  TEST THE MAXIMUM LEVEL OF A MIX, DEFINED IN A MIXFILE\n"
	    "AND SUGGEST A GAIN FACTOR TO AVOID OVERLOAD, IF NECESSARY\n\n"
    	"USAGE: submix getlevel 1   mixfile             [-sSTART] [-eEND]\n"
    	"USAGE: submix getlevel 2-3 mixfile outtextfile [-sSTART] [-eEND]\n\n"
    	"MODES...\n"
    	"1) finds maximum level of mix.\n"
    	"2) finds locations of clipping in mix.\n"
    	"3) finds locations of clipping, and maxlevel, in mix.\n"
      	"START gives starttime START (to start mixing later than zero).\n"
    	"END   gives output endtime END (to stop mix before its true end).\n"
  	     "OUTTEXTFILE  stores clipping locations (& maxlevel, in mode 3).\n\n"
    	"You can alter the overall level of a mix with 'submix attenuate'.\n");
	} else if(!strcmp(str,"fileformat")) {		
	sprintf(errstr,
	"MIXFILES CONSIST OF LINES WITH ONE OF THE FOLLOWING FORMATS\n"
	"sndname starttime_in_mix  chans  level\n"
	"sndname starttime_in_mix  1      level       pan\n"
	"sndname starttime_in_mix  2      left_level  left_pan  right_level  right_pan\n\n"
	"SNDNAME is name of a MONO or STEREO sndfile: ALL MUST BE OF SAME SAMPLING RATE.\n"
	"CHANS   is number of channels in this soundfile (1 or 2 ONLY)\n"
	"LEVEL   is loudness, as number (1 = unity gain) or dB (0dB = unity gain)\n"
	"        Mono AND stereo files MAY have a SINGLE level parameter (NO pan data).\n"
	"        In this case, MONO files in STEREO mixes are panned centrally.\n"
	"        OTHERWISE....\n"
	"        MONO files   must have 1 level & 1 pan parameter (ONLY).\n"
	"        STEREO files MUST have 2 level & 2 pan params, 1 for each channel.\n"
	"PAN     is spatial positioning of file (or file channel) in output mix.\n"
	"        -1 HArd Left : 0 Centre : 1 Hard Right\n"
	"        < -1 hard left & attenuated : > 1 hard right & attenuated.\n"
	"ALSO....\n"
	"1) The mixfile list need NOT be in starttime order.\n"
	"2) Silence at start of mix IGNORED.(splice on afterwards if needed).\n"
	"3) With exclusively mono inputs, with NO pan information, OR \n"
	"   when ALL panned hard left, or ALL hard right, output is MONO.\n"
	"   All other situations produce stereo output.\n"
	"4) TAKE CARE WHEN PANNING BOTH CHANNELS OF A STEREO FILE.\n"
	"   The channel contributions sum, so e.g. if both channels are\n"
	"   panned to same position without attenuation, overload possible.\n"
	"5) You may test for maximum level in your mix output with submix GETLEVEL.\n"
	"   Reduce the mixfile level, if necessary, using 'submix attenuate'.\n"
	"6) You may put comment lines in mixfiles : Start such line with a ';'\n"
	"   Blank lines are ignored.\n");
	} else if(!strcmp(str,"dummy")) {		
	    sprintf(errstr,
	    "CONVERT A LIST OF SNDFILES INTO A BASIC MIXFILE (FOR EDITING)\n\n"
    	"USAGE: submix dummy mode infile1 infile2 [infile3..] mixfile\n"
		"mode 1 - all files start at time zero.\n"
		"mode 2 - each file starts where previous file ends.\n"
		"mode 3 - (mono) files, first to left, remainder to right.\n");
//TW UPDATES
	} else if(!strcmp(str,"ongrid")) {		
	    sprintf(errstr,
	    "CONVERT LISTED SNDFILES TO BASIC MIXFILE ON TIMED GRID (FOR EDITING)\n\n"
    	"USAGE: submix ongrid infil1 infil2 [infil3..] outmixfile gridfile\n\n"
 		"GRIDFILE a list of times (one for each input sound) of sounds in mix\n"
		"         OR a list with some times preceded by 'x' (no space after 'x')\n"
		"         where 'x' marks a time to actually use \n"
		"         (other grid times being ignored).\n\n"
		"Numbers, or gridpoint names, may follow times (on same line ONLY)\n\n"
		"IF NO SOUND USED AT TIME ZERO, mix will skip to first snd actually used.\n"
		"To avoid this, use a SILENT FILE at time zero.\n");
	} else if(!strcmp(str,"atstep")) {		
	    sprintf(errstr,
	    "CONVERT LISTED SNDFILES TO BASIC MIXFILE WITH FIXED TIMESTEP BETWEEN ENTRIES\n\n"
  	   	"USAGE: submix atstep infile1 infile2 [infile3..] outmixfile step\n\n"
		"STEP is time, in seconds, between entry of each file.\n\n");
	} else if(!strcmp(str,"faders")) {		
	    sprintf(errstr,
		"MIX SEVERAL MONO OR STEREO FILES USING TIME-CHANGING BALANCE-FUNCTION\n\n"
    	"USAGE: submix faders inf1 inf2 [inf3..] outf balance-data envelope-data\n\n"
    	"(NB do not mix mono and stereo in the input)\n"
		"BALANCE-DATA  is a list of value sets.\n"
		"              Each consisting of a time, followed by the\n"
		"              RELATIVE level of each file in the mix, at that time.\n\n"
		"              Whatever set of numbers is used, THESE ARE SCALED\n"
		"              so the total set of levels used ON ANY LINE adds up to 1.0\n"
		"              (This means that if all signals are at max level\n"
		"              the output will never exceed max level)\n\n"
		"              You can't use this data to vary the OVERALL level.\n"
		"              and, in particular, the values cannot ALL be zero.\n\n"
		"ENVELOPE-DATA is the loudness envelope to apply to the TOTAL sound.\n"
		"              and this (also) can vary over time.\n");

	} else if(!strcmp(str,"timewarp")) {		
		sprintf(errstr,
		"TIMEWARP THE DATA IN A MIXFILE\n\n"
		"USAGE:  submix timewarp 1    inmixfile outmixfile\n"
		"OR:     submix timewarp 2-5  inmixfile outmixfile   [-sstartline] [-eendline]\n"
		"OR:     submix timewarp 6-16 inmixfile outmixfile Q [-sstartline] [-eendline]\n\n"
		"STARTLINE: line at which warping begins (default: 1st in file)\n"
		"ENDLINE  : line at which warping ends  (default: last in file).\n"
		"MODES ARE....\n"
		"1)  SORT INTO TIME ORDER.\n"
		"2)  REVERSE TIMING PATTERN:  e.g. rit. of sound entries becomes an accel.\n"
		"3)  REVERSE TIMING PATTERN & ORDER OF FILENAMES.\n"
		"4)  FREEZE TIMEGAPS          between sounds, at FIRST timegap value.\n"
		"5)  FREEZE TIMEGAPS & NAMES  ditto, and all files take firstfile name.\n"
		"6)  SCATTER ENTRY TIMES      about orig vals. Q is scattering: Range(0-1).\n"
		"7)  SHUFFLE UP ENTRY TIMES   shuffle times in file forward by time Q secs.\n"
		"8)  ADD TO TIMEGAPS          add fixed val Q secs, to timegaps between sounds.\n"
		"9)  CREATE FIXED TIMEGAPS 1  between all sounds,timegap = Q secs\n"
		"10) CREATE FIXED TIMEGAPS 2  startval+Q,startval+2Q  etc\n"
		"11) CREATE FIXED TIMEGAPS 3  startval*Q startval*2Q etc\n"
		"12) CREATE FIXED TIMEGAPS 4  startval*Q     startval*Q*Q    etc\n"
		"13) ENLARGE TIMEGAPS 1       multiply them by Q.\n"
		"14) ENLARGE TIMEGAPS 2       by +Q, +2Q,+3Q  etc\n"
		"15) ENLARGE TIMEGAPS 3       by *Q *2Q *3Q\n"
		"16) ENLARGE TIMEGAPS 4       by *Q, *Q*Q, *Q*Q*Q  etc. (CARE!!)\n\n");
	} else if(!strcmp(str,"shuffle")) {		
		sprintf(errstr,
		"SHUFFLE THE DATA IN A MIXFILE\n\n"
		"USAGE: submix shuffle 1-6 inmixfile outmixfile         [-sstartl] [-eendl]\n"
		"OR:    submix shuffle 7   inmixfile outmixfile newname [-sstartl] [-eendl] [-x]\n\n"
		"STARTL: line at which shuffling begins (default: 1st in file)\n"
		"ENDL  : line at which shuffling ends  (default: last in file).\n"
		"MODES ARE....\n"
		"1)  DUPLICATE EACH LINE.\n"
		"2)  REVERSE ORDER OF FILENAMES.\n"
		"3)  SCATTER ORDER OF FILENAMES.\n"
		"4)  REPLACE SOUNDS IN SELECTED LINES WITH SOUND IN STARTLINE.\n"
		"5)  OMIT LINES           (closing up timegaps appropriately)\n"
		"6)  OMIT ALTERNATE LINES (closing up timegaps appropriately)\n"
		"                         In modes 5 & 6 mix must be in correct time-order.\n"
		"                         mixfiles can be time-ordered using timewarp mode 1\n"
		"7)  DUPLICATE AND RENAME: duplicate each line with new sound, newname.\n\n"
		"                         Program checks 'newname' is compatible sndfile, BUT,\n"
		"-x  flag TURNS OFF 'newname' checking in mode 7.\n");
	} else if(!strcmp(str,"attenuate")) {		
		sprintf(errstr,
		"ALTER THE OVERALL LEVEL OF A MIXFILE.\n\n"
		"USAGE:  submix attenuate inmixfile outmixfile gainval [-sstartline] [-eendline]\n\n"
		"GAINVAL must be > 0.0\n"
		"STARTLINE: line at which attenuation begins (default: 1st in file)\n"
		"ENDLINE  : line at which attenuation ends  (default: last in file).\n"
		"You can test the overall level of a mix with 'submix getlevel'\n");
	} else if(!strcmp(str,"spacewarp")) {		
		sprintf(errstr,
		"ALTER THE SPATIAL DISTRIBUTION OF A MIXFILE.\n\n"
		"USAGE: submix spacewarp 1-2 inmixfile outmixfile Q     [-sstartl] [-eendl]\n"
		"OR:    submix spacewarp 4-6 inmixfile outmixfile Q1 Q2 [-sstartl] [-eendl]\n"
		"OR:    submix spacewarp 7   inmixfile outmixfile\n"
		"OR:    submix spacewarp 8   inmixfile outmixfile Q\n"
		"STARTL: line at which warping begins (default: 1st in file)\n"
		"ENDL  : line at which warping ends  (default: last in file).\n"
		"MODES ARE....\n"
		"1) SOUNDS TO SAME POSITION           Q is position. (stereo files become mono)\n"
		"2) NARROW SPATIAL SPREAD             Q is a +ve number < 1.0\n"
		"3) SEQUENCE POSITIONS LEFTWARDS    over range Q1-Q2 (stereo files become mono)\n"
		"4) SEQUENCE POSITIONS RIGHTWARDS   over range Q1-Q2 (stereo files become mono)\n"
		"5) RANDOM-SCATTER POSITIONS      within range Q1-Q2 (stereo files become mono)\n"
		"6) RANDOM, BUT ALTERNATE TO L/R OF THE CENTRE OF THE SPATIAL RANGE SPECIFIED.\n"
		"                                        range Q1-Q2 (stereo files become mono)\n"
		"7) INVERT STEREO IN ALTERNATE LINES OF MIXFILE: (Use to avoid clipping).\n"
		"8) INVERT STEREO IN SPECIFIED LINE OF MIXFILE   Q is line number.\n");
	} else if(!strcmp(str,"sync")) {		
		sprintf(errstr,
		"SYNCHRONISE SOUNDFILES IN A MIXFILE,\n"
		"OR GENERATE SUCH A MIXFILE FROM A LIST OF SNDFILES.\n\n"
		"USAGE: submix sync mode intextfile outmixfile\n\n"
		"INTEXTFILE is list of sndfiles OR an existing mixfile.\n\n"
		"MODES ARE....\n"
		"1) SYNC SNDFILE MIDTIMES.\n"		
		"2) SYNC SNDFILE ENDTIMES.\n");
	} else if(!strcmp(str,"syncattack")) {		
		sprintf(errstr,
		"SYNCHRONISE ATTACKS OF SOUNDFILES, IN A MIXFILE,\n"
		"OR GENERATE SUCH A MIXFILE FROM A LIST OF SNDFILES.\n\n"
		"USAGE: submix syncattack intextfile outmixfile  [-wdiv] [-p]\n\n"
		"INTEXTFILE is a list of sndfiles OR an existing mixfile.\n"		
		"           with a sndfilelist (only), each sndname MAY be followed by 2 times,\n"
		"           which limit the search area for the sound's attack.\n"
		"-w         DIV is factor shortening window which scans for the attack.\n"
   		"           It can be 2,4,8,16, or 32 ONLY.\n"
   		"-p         Program finds peak-power segment, before locating its max sample.\n"
   		"           Default: program looks purely for maxsample.\n\n"
		"The program estimates output levels required to prevent clipping,\n"
		"But estimate may be over-cautiously low: adjust with 'submix attenuate'.\n");
	} else if(!strcmp(str,"merge")) {		
		sprintf(errstr,
		"QUICK MIX OF 2 SNDFILES (Mono or Stereo only).\n\n"
		"USAGE: submix merge sndfile1 sndfile2 outfile\n"
		"          [-sstagger] [-jskip] [-kskew] [-bstart] [-eend]\n"
		"\n"
		"STAGGER ...2nd file enters, 'stagger' secs after first.\n"
		"SKIP ......skip by 'skip' secs into the 2nd file, before starting to mix.\n"
		"SKEW.......1st sound has 'skew' times more gain than 2nd.\n"
		"START .....Start the mix at the time specified.\n"
		"END........Stop the mix at the time specified.\n"
		"Stagger and Skip are approximated to within about one hundredth of a second.\n");
//TW UPDATE
	} else if(!strcmp(str,"mergemany")) {		
		sprintf(errstr,
		"QUICK MIX OF SEVERAL SNDFILES (WITH SAME NUMBER OF CHANNELS).\n\n"
		"USAGE: submix mergemany sndfile1 sndfile2 [sndfile3 ....] outfile\n");
	} else if(!strcmp(str,"balance")) {		
		sprintf(errstr,
		"MIX BETWEEN 2 SNDFILES USING A BALANCE FUNCTION.\n\n"
		"USAGE: submix balance sndfile1 sndfile2 outfile\n"
		"                            [-kbalance] [-bstart] [-eend]\n\n"
		"(files may or may not have different number of channels).\n"
		"\n"
		"BALANCE ...Describes the relative level of the two sounds. (Range 0-1)\n"
		"......File 1 level is multiplied by the balance function.\n"
		"......File 2 level is multiplied by the inverse of the balance function.\n"
		"Balance may vary over time.\n\n"
		"START .....Start the mix at the time specified.\n"
		"END........Stop the mix at the time specified.\n");	
	} else if(!strcmp(str,"crossfade")) {		
		sprintf(errstr,
		"QUICK CROSSFADE BETWEEN SNDFILES (WITH SAME NUMBER OF CHANNELS).\n\n"
		"USAGE: submix crossfade 1 sndfile1 sndfile2 outfile\n\t\t[-sSTAGGER] [-bBEGIN] [-eEND]\n"
		"OR:    submix crossfade 2 sndfile1 sndfile2 outfile\n\t\t[-sSTAGGER] [-bBEGIN] [-eEND] [-pPOWFAC]\n\n"
		"MODES ARE...\n"
		"1) Linear crossfade.\n"
		"2) Cosinusiodal crossfade\n\n"
		"Crossfade is from sndfile1 towards sndfile2.\n\n"
	    "STAGGER   2nd file starts 'stagger' secs. after 1st. (Default: 0)\n"
		"BEGIN     crossfade starts at BEGIN secs. (>Stagger) (Default: 0)\n"
		"END       crossfade ends at END secs. (>Begin) (Default: end of shortest file)\n"
		"          If crossfade ends before end of file2, remainder of file2 plays on.\n"
	    "POWFAC    crossfade skew.\n"
		"          if powfac = 1, cosinusoidal-crossfade is normal.\n"
		"          in range %.2lf - 1, cosin-fade begins rapidly then slows.\n"
		"          in range 1 - %.0lf, cosin-fade begins slowly, then speeds up.\n\n"
 		"Stagger approximated in c. hundredths of a sec. For more precise stagger, \n" 
		"splice silence to start of sndfile2 & use stagger 0 (or use 'submix mix').\n\n"
		"(Only spectral morphing will create true morph between 2 sounds).\n",MIN_MCR_POWFAC,MAX_MCR_POWFAC);
	} else if(!strcmp(str,"interleave")) {		
		sprintf(errstr,
		"INTERLEAVE MONO FILES TO MAKE MULTICHANNEL OUTFILE.\n\n"
		"USAGE: submix interleave sndfile1 sndfile2 [sndfile3 sndfile4] outfile\n\n"
		"MAx number of channels in output is %d\n"
		"First sndfile goes to left channel of stereo, (or channel 1 of 4), etc.\n",MAX_MI_OUTCHANS);
	} else if(!strcmp(str,"inbetween")) {		
		sprintf(errstr,
		"GENERATE A SET OF SOUNDS IN-BETWEEN THE 2 INPUT SOUNDS\n"
		"      THROUGH WEIGHTED MIXES OF THE INPUT SOUNDS,\n"
		"         FROM MOSTLY SOUND1 TO MOSTLY SOUND2.\n\n"
		"USAGE: submix inbetween 1 infile1  infile2  outname  count\n"
		"OR:    submix inbetween 2 infile1  infile2  outname  ratios\n\n"
		"OUTNAME  is your GENERIC name for the output sndfiles.\n"
		"         New soundfiles will be called outname001 outname002 etc\n\n"
		"COUNT    If a single number (COUNT) is given to the program,\n"
		"         it generates amplitude ratios for the new sounds, automatically,\n"
		"         and COUNT is the number of inbetween outfiles to produce.\n\n"
		"RATIOS   A ratio, or a list of ratios in a textfile. These are\n"
		"         the level of file2 RELATIVE to file 1,for each new outfile,\n"
		"         as FRACTIONS (Range 0-1)\n"
		"         There must be an even number of values and in ascending order.\n\n"
		"Ensure NONE of the files to be created already exists!!\n");
	} else if(!strcmp(str,"inbetween2")) {		
		sprintf(errstr,
		"GENERATE A SET OF SOUNDS IN-BETWEEN THE 2 INPUT SOUNDS\n"
		"      THROUGH INTERPOLATION PEGGED TO ZERO_CROSSINGS.\n\n"
		"USAGE: submix inbetween2 infile1  infile2  outname  count  cutoff\n"
		"OUTNAME  is your GENERIC name for the output sndfiles.\n"
		"         New soundfiles will be called outname001 outname002 etc\n\n"
		"COUNT    is the number of inbetween outfiles to produce.\n\n"
		"CUTOFF   Is the frquency above which 'cycles' are ignored: \n"
		"         usually noise, they are incorporated into other cycles.\n\n"
		"Ensure NONE of the files to be created already exists!!\n");
	} else if(!strcmp(str,"addtomix")) {		
		sprintf(errstr,
		"ADD SOUNDFILES (at max level and time end-of-file) TO EXISTING MIXFILE.\n\n"
		"USAGE: submix addtomix mixfile sndfile1 [sndfile2 ........] outfile\n");
	} else if(!strcmp(str,"model")) {		
		sprintf(errstr,
		"REPLACE SOUNDFILES IN EXISTING MIXFILE.\n\n"
		"USAGE: submix model mixfile sndfile1 [sndfile2 ........] outfile\n");
	} else if(!strcmp(str,"pan")) {		
		sprintf(errstr,
		"PAN A MIXFILE.\n\n"
		"USAGE: submix pan inmixfile outmixfile pan\n\n"
		"PAN may vary over time\n"
		"    in this case, soundfiles in mix are POSITIONED differently\n"
		"    depending on time they begin, and value in PAN file at that time.\n"
		"    BUT the sounds THEMSELVES will not be panned (in the final mixdown)\n"); 
	} else
		sprintf(errstr,"Unknown option '%s'\n",str);
	return(USAGE_ONLY);
}

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

int usage3(char *str1,char *str2)
{	
	if(!strcmp(str1,"test"))		
		return(CONTINUE);
	else if(!strcmp(str1,"fileformat"))	{
		sprintf(errstr,"Too many parameters on command line.\n");
		return(USAGE_ONLY);
	} else
		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);
}


/************************* MIX_ATTEN ******************************/

int mix_atten(dataptr dz)
{
	int    exit_status;
	int   lineno, total_words;

 	total_words = 0;
	for(lineno=0;lineno<dz->linecnt;lineno++) {
		if((exit_status = adjust_levels(lineno,total_words,dz))<0) {
			return(exit_status);
		}
		total_words += dz->wordcnt[lineno];
	}
	return(FINISHED);
}

/********************** ADJUST_LEVELS ****************************/
 
int adjust_levels(int lineno,int total_words,dataptr dz)
{
	int exit_status;
	int wordcnt = dz->wordcnt[lineno], len;
	char *thisword, temp[200]; 
	double level;
	int wordno = total_words + MIX_LEVELPOS;

	thisword = dz->wordstor[wordno];
	if((exit_status = get_level(thisword,&level))<0)
		return(exit_status);
	level *= dz->param[MIX_ATTEN];
	sprintf(temp,"%lf",level);
	if((len = (int)strlen(temp)) > (int)strlen(thisword)) {
		if((dz->wordstor[wordno] = (char *)realloc((char *)dz->wordstor[wordno],(len+1)))==NULL)
			return(MEMORY_ERROR);
	}	
	strcpy(dz->wordstor[wordno],temp);
	if(wordcnt <= MIX_RLEVELPOS)
		return(FINISHED);

	wordno = total_words + MIX_RLEVELPOS;
	thisword = dz->wordstor[wordno];
	if((exit_status = get_level(thisword,&level))<0)
		return(exit_status);
	level *= dz->param[MIX_ATTEN];
	sprintf(temp,"%lf",level);
	if((len = (int)strlen(temp)) > (int)strlen(thisword)) {
		if((dz->wordstor[wordno] = (char *)realloc((char *)dz->wordstor[wordno],(len+1)))==NULL)
			return(MEMORY_ERROR);
	}	
	strcpy(dz->wordstor[wordno],temp);
	return(FINISHED);
}

//TW UPDATE: NEW FUNCTIONS
/********************** READ_MIXGRID_FILE ****************************/
 
int read_mixgrid_file(char *str,dataptr dz)
{
	int linecnt = 0, timecnt = 0, is_marked = 0;
	char temp[200], *q, *p;
	double time, last_time = 0.0;
	int arraysize = BIGARRAY;
	FILE *fp;

	if((fp = fopen(str,"r"))==NULL) {
		sprintf(errstr,"Failed to open file %s to read the grid data\n",str);
		return(DATA_ERROR);
	}
	if((dz->parray[0] = (double *)malloc(arraysize * sizeof(double)))==NULL) {
		sprintf(errstr,"INSUFFICIENT MEMORY for grid data.\n");
		return(MEMORY_ERROR);
	}
	while(fgets(temp,200,fp)==temp) {
		q = temp;
		linecnt++;
		if(!get_word_from_string(&q,&p))
			continue;
		if(*p == 'x') {
			p++;
			if(!is_marked) {
				timecnt = 0;
				is_marked = 1;
			}
		} else if(is_marked)
			continue;
		if(sscanf(p,"%lf",&time)!=1) {
			sprintf(errstr,"Failed to find a time at line %d\n",linecnt);
			return(DATA_ERROR);
		}
		if(time < 0.0) {
			sprintf(errstr,"Invalid timevalue (%lf) at line %d\n",time,linecnt);
			return(DATA_ERROR);
		}
		if(timecnt>0) {
			if(time < last_time) {
				if(is_marked)
					sprintf(errstr,"Marked times not in ascending order at line %d time %lf\n",linecnt,time);
				else
					sprintf(errstr,"Times not in ascending order at line %d time %lf\n",linecnt,time);
				return(DATA_ERROR);
			}
		}
		last_time = time;
		dz->parray[0][timecnt] = time;
		if(++timecnt >= arraysize) {
			arraysize += BIGARRAY;
			if((dz->parray[0] = (double *)realloc((char *)dz->parray[0],arraysize * sizeof(double)))==NULL) {
				sprintf(errstr,"INSUFFICIENT MEMORY for grid data.\n");
				return(MEMORY_ERROR);
			}
		}
	}						
	if(timecnt != dz->infilecnt) {
		if(timecnt < dz->infilecnt) {
			if(is_marked)
				sprintf(errstr,"Insufficient times marked (%d) for the number of input files (%d).\n",
				timecnt,dz->infilecnt);
			else
				sprintf(errstr,"Insufficient times listed (%d) for the number of input files (%d).\n",
				timecnt,dz->infilecnt);
			return(DATA_ERROR);
		} else {
			if(is_marked)
				fprintf(stdout,"WARNING: Too many times marked (%d) for the number of input files (%d).\n",
				timecnt,dz->infilecnt);
			else
				fprintf(stdout,"WARNING: Too many times listed (%d) for the number of input files (%d).\n",
				timecnt,dz->infilecnt);
			fflush(stdout);
		}
	}
	if((dz->parray[0] = (double *)realloc((char *)dz->parray[0],dz->infilecnt * sizeof(double)))==NULL) {
		sprintf(errstr,"INSUFFICIENT MEMORY for reallocate storage for grid data.\n");
		return(MEMORY_ERROR);
	}
	if(!flteq(dz->parray[0][0],0.0)) {
		fprintf(stdout,"WARNING: First time used is NOT AT ZERO :  Mix will start at start of 1st file\n");
		fprintf(stdout,"WARNING: and will be curtailed by %.4lf secs at start.\n",dz->parray[0][0]);
		fflush(stdout);
	}
	return(FINISHED);
}

/*************************** READ_AND_SCALE_BALANCE_DATA *******************************/

int read_and_scale_balance_data(char *filename,dataptr dz)
{
	FILE *fp;
	double *p, sum, errtime = 1.0/dz->infile->srate, lasttime = 0.0;
	int datalen = dz->infilecnt + 1;
	int arraysize = BIGARRAY;
	char temp[200], *q;
	int n = 0, m;
	if((fp = fopen(filename,"r"))==NULL) {
		sprintf(errstr,	"Can't open textfile %s to read data.\n",filename);
		return(DATA_ERROR);
	}
	if((dz->parray[0] = (double *)malloc(arraysize * sizeof(double)))==NULL) {
		sprintf(errstr,"INSUFFICIENT MEMORY for data.\n");
		return(MEMORY_ERROR);
	}
	p = dz->parray[0];
	while(fgets(temp,200,fp)==temp) {
		q = temp;
		while(*q != ENDOFSTR) {
			if(!isspace(*q))
				break;
			q++;
		}
		if(*q == ';')
			continue;
		if(invalid_data_items(temp))
			return(DATA_ERROR);
		while(get_float_from_within_string(&q,p)) {
			if(n%datalen==0)	{ /* time value */
				if(n==0) {
					lasttime = *p;
					if(*p != 0.0) {
						sprintf(errstr,"Data in datafile %s must begin at time zero\n",filename);
						return(DATA_ERROR);
					}
				} else {
					if (*p < lasttime + errtime) {
						sprintf(errstr,"Times %lf and %lf too close in datafile %s\n",*p,lasttime,filename);
						return(DATA_ERROR);
					}
				}
			} else if(*p < 0.0) {
				sprintf(errstr,"Negative gain values not permitted in datafile %s\n",filename);
				return(DATA_ERROR);
			}
			p++;
			if(++n >= arraysize) {
				arraysize += BIGARRAY;
				if((dz->parray[0] = (double *)realloc((char *)dz->parray[0],arraysize * sizeof(double)))==NULL) {
					sprintf(errstr,"INSUFFICIENT MEMORY to reallocate data table.\n");
					return(MEMORY_ERROR);
				}
				p = dz->parray[0] + n;		
			}
		}
		if(n%datalen == 0) {
			sum = 0.0;
			for(m=1;m <= dz->infilecnt;m++)
				sum += *(p-m);
			if(sum <= 0.0) {
				sprintf(errstr,"levels are zero at line %d\n",(n/datalen)+1);
				return(DATA_ERROR);
			}
			sum = 1.0/sum;
			for(m=1;m <= dz->infilecnt;m++)
				*(p-m) *= sum;	/* scale all gain values */
		}
	}
	if(n == 0) {
		sprintf(errstr,"No data in textdata file %s\n",filename);
		return(DATA_ERROR);
	}
	if(n%datalen != 0) {
		sprintf(errstr,"Data in textdata file %s is not grouped correctly\n",filename);
		return(DATA_ERROR);
	}
	if((dz->parray[0] = (double *)realloc((char *)dz->parray[0],n * sizeof(double)))==NULL) {
		sprintf(errstr,"INSUFFICIENT MEMORY to reallocate data table.\n");
		return(MEMORY_ERROR);
	}
	if(fclose(fp)<0) {
		fprintf(stdout,"WARNING: Failed to close input textfile %s.\n",filename);
		fflush(stdout);
	}
	dz->itemcnt = n;
	p  = dz->parray[0];
	return(FINISHED);
}

/****************************** MIXMODEL_CONSISTENCY *********************************/

int mixmodel_consistency(dataptr dz)
{
	infileptr fpq;
	int exit_status;
	int n, m, sndfilecnt, totalwords = 0, thischans, filestart;
	double maxamp, maxloc;
	int maxrep;
	int getmax = 0, getmaxinfo = 0;
	if(dz->infile->filetype != MIXFILE) {
		sprintf(errstr,"The FIRST FILE must be a MIXFILE.\n");
		return(USER_ERROR);
	}
	for(n=0;n<dz->linecnt;n++)
		totalwords += dz->wordcnt[n];
	filestart = totalwords;
	dz->itemcnt = filestart;
	totalwords = 0;
	if((fpq = (infileptr)malloc(sizeof(struct filedata)))==NULL) {
		sprintf(errstr,"INSUFFICIENT MEMORY to store header data of soundfiles.\n");
		return(MEMORY_ERROR);
	}
	for(n=0,m=filestart,sndfilecnt=1;n<dz->linecnt;n++,m++,sndfilecnt++) {
		if(sndfilecnt >= dz->infilecnt) {
			sprintf(errstr,"Insufficient soundfiles entered\n");
			return(DATA_ERROR);
		}
		if((sscanf(dz->wordstor[totalwords + 2],"%d",&thischans)) != 1) {
			sprintf(errstr,"Failed to get channel count in line %d of mixfile\n",m);
			return(DATA_ERROR);
		}
		if((exit_status = readhead(fpq,dz->ifd[sndfilecnt],dz->wordstor[m],&maxamp,&maxloc,&maxrep,getmax,getmaxinfo))<0)
			return(exit_status);
		if(fpq->channels != thischans) {
			sprintf(errstr,"channel count in line %d of mixfile incompatible with entered file %s\n",m,dz->wordstor[m]);
			return(DATA_ERROR);
		}
		totalwords += dz->wordcnt[n];
	}
	if(sndfilecnt != dz->infilecnt) {
		sprintf(errstr,"Too many soundfiles entered\n");
		return(DATA_ERROR);
	}
	return FINISHED;
}

/************** READ_INBETWEEN_RATIOS ***********/

int read_inbetween_ratios(char *str,dataptr dz)
{
	char temp[200], *p;
	double q;
	int n;
	int arraysize = BIGARRAY;
	FILE *fp;

	if((fp = fopen(str,"r"))==NULL) {
		sprintf(errstr,"Failed to open file %s to read the inbetween ratios\n",str);
		return(DATA_ERROR);
	}
	if((dz->brk = (double **)malloc(sizeof(double *)))==NULL) {
		sprintf(errstr,"INSUFFICIENT MEMORY for grid data.\n");
		return(MEMORY_ERROR);
	}
	if((dz->no_brk = (char *)malloc(sizeof(char)))==NULL) {
		sprintf(errstr,"INSUFFICIENT MEMORY for grid data.\n");
		return(MEMORY_ERROR);
	}
	if((dz->iparam = (int *)malloc(sizeof(int)))==NULL) {
		sprintf(errstr,"INSUFFICIENT MEMORY for grid data.\n");
		return(MEMORY_ERROR);
	}
	if((dz->brk[0] = (double *)malloc(arraysize * sizeof(double)))==NULL) {
		sprintf(errstr,"INSUFFICIENT MEMORY for grid data.\n");
		return(MEMORY_ERROR);
	}
	n = 0;
	while(fgets(temp,200,fp)==temp) {
		p = temp;
		while(get_float_from_within_string(&p,&q)) {
			if(n > MAXBETWEEN) {
				sprintf(errstr,"Too many inbetween-ratios in file: maximum %d\n",MAXBETWEEN);
				return(USER_ERROR);
			}
			if(n >= arraysize) {
				arraysize += BIGARRAY;
				if((dz->brk[0] = (double *)realloc((char *)dz->brk[0],arraysize * sizeof(double)))==NULL) {
					sprintf(errstr,"INSUFFICIENT MEMORY for inbetween ratios.\n");
					return(MEMORY_ERROR);
				}
			}
			if(q < 0.0 || q > 1.0) {
				sprintf(errstr,"Inbetween value (%lf) out of range (0-1) at item %d\n",q,n+1);
				return(DATA_ERROR);
			}
			dz->brk[0][n] = q;
			n++;
		}
	}						
	if((dz->brk[0] = (double *)realloc((char *)dz->brk[0],n * sizeof(double)))==NULL) {
		sprintf(errstr,"INSUFFICIENT MEMORY for reallocate storage for grid data.\n");
		return(MEMORY_ERROR);
	}
	dz->no_brk[INBETW] = FALSE;
	dz->iparam[INBETW] = n;
	return(FINISHED);
}

/**************************** CYC_PREPROP *************************/

int cyc_preprop(dataptr dz)
{
	int n0, n1, lastn0, lastn1, c0, c1, cyclecnt;
	int OK;
	float *ibuf0 = dz->sampbuf[0], *ibuf1 = dz->sampbuf[1];
	int *cycs0, *cycs1, *lens0, *lens1;
	int n;
	int hifrq_cut = (int)round((double)dz->infile->srate/dz->param[BTWN_HFRQ]);

	if((dz->ssampsread = fgetfbufEx(ibuf0,dz->buflen,dz->ifd[0],0)) <= 0) {
		sprintf(errstr,"Failed to read data: Infile 1\n");
		return(SYSTEM_ERROR);
	}
	if((dz->ssampsread = fgetfbufEx(ibuf1,dz->buflen,dz->ifd[1],0)) <= 0) {
		sprintf(errstr,"Failed to read data: Infile 2\n");
		return(SYSTEM_ERROR);
	}
	n0 = 0;
	while(ibuf0[n0] == 0) {
		if(++n0 >= dz->insams[0]) {
			sprintf(errstr,"No signal in first soundfile\n");
			return(DATA_ERROR);
		}
	}
	if(ibuf0[n0] < 0) {		/* force positive phase */
		for(n=0;n<dz->insams[0];n++)
			ibuf0[n] = (float)(-ibuf0[n]);
	}
	n1 = 0;
	while(ibuf1[n1] == 0) {
		if(++n1 >= dz->insams[1]) {
			sprintf(errstr,"No signal in second soundfile\n");
			return(DATA_ERROR);
		}
	}
	if(ibuf1[n1] < 0) {	/* force positive phase */
		for(n=0;n<dz->insams[1];n++)
			ibuf1[n] = (float)(-ibuf1[n]);
	}
	n0 = 0;
	n1 = 0;
	OK = 1;
	c0 = 0;
	c1 = 0;
	while(OK) {				/* This loop counts wavesets, with upper frq limit */
		lastn0 = n0;
		c0++;
		lastn1 = n1;
		c1++;
		while(OK) {
			while(ibuf0[n0] >= 0) {
				if(++n0 >= dz->insams[0]) {
					OK = 0;
					break;
				}
			}
			if(OK) {
				while(ibuf0[n0] < 0) {
					if(++n0 >= dz->insams[0]) {
						OK = 0;
						break;
					}
				}
			}
			if(!OK || (n0 - lastn0 > hifrq_cut))
				break;
		}
		while(OK) {
			while(ibuf1[n1] >= 0) {
				if(++n1 >= dz->insams[1]) {
					OK = 0;
					break;
				}
			}
			if(OK) {
				while(ibuf1[n1] < 0) {
					if(++n1 >= dz->insams[1]) {
						OK = 0;
						break;
					}
				}
			}
			if(!OK  || (n1 - lastn1 > hifrq_cut))
				break;
		}
	}

	if(c1 != c0) {
		sprintf(errstr,"Unequal cycle counts\n");
		return(PROGRAM_ERROR);
	}
	cyclecnt = c0;
	if((dz->lparray[0] = malloc(cyclecnt * sizeof(int)))==NULL) {
		sprintf(errstr,"Insufficient memory to store cycle positions for sound 1\n");
		return(MEMORY_ERROR);
	}
	if((dz->lparray[1] = malloc(cyclecnt * sizeof(int)))==NULL) {
		sprintf(errstr,"Insufficient memory to store cycle positions for sound 2\n");
		return(MEMORY_ERROR);
	}
	if((dz->lparray[2] = malloc((cyclecnt - 1) * sizeof(int)))==NULL) {
		sprintf(errstr,"Insufficient memory to store cycle lengths for sound 1\n");
		return(MEMORY_ERROR);
	}
	if((dz->lparray[3] = malloc((cyclecnt -1) * sizeof(int)))==NULL) {
		sprintf(errstr,"Insufficient memory to store cycle lengths for sound 2\n");
		return(MEMORY_ERROR);
	}
	cycs0 = dz->lparray[0];
	cycs1 = dz->lparray[1];
	lens0 = dz->lparray[2];
	lens1 = dz->lparray[3];

	n0 = 0;
	n1 = 0;
	c0 = 0;
	c1 = 0;
	OK = 1;
	while(OK) {			/* This loop stores the zero-crossing positions */
		cycs0[c0++] = n0;
		cycs1[c1++] = n1;
		lastn0 = n0;
		lastn1 = n1;
		if(c0 >= cyclecnt)
			OK = 0;
		else {
			while(OK) {
				while(ibuf0[n0] >= 0)
					n0++;
				while(ibuf0[n0] < 0)
					n0++;
				if(n0 - lastn0 > hifrq_cut)
					break;
			}
			while(OK) {
				while(ibuf1[n1] >= 0)
					n1++;
				while(ibuf1[n1] < 0)
					n1++;
				if(n1 - lastn1 > hifrq_cut)
					break;
			}
		}
	}
	for(n = 1;n < cyclecnt; n++) {
		lens0[n-1] = cycs0[n] - cycs0[n-1];
		lens1[n-1] = cycs1[n] - cycs1[n-1];
	}
	dz->itemcnt = cyclecnt;
	return FINISHED;
}

int invalid_data_items(char *str) 
{
	char *p = str;
	while(*p != ENDOFSTR) {
		if(!(isspace(*p) || isdigit(*p) || *p =='.' || *p == '-')) {
			sprintf(errstr,"Invalid (non-numeric) data in balance function.\n");
			return 1;
		}
		p++;
	}
	return 0;
}
