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



#include <stdio.h>
#include <stdlib.h>
#include <structures.h>
#include <tkglobals.h>
#include <globcon.h>
#include <cdpmain.h>
#include <sndinfo.h>
#include <processno.h>
#include <modeno.h>
#include <pnames.h>
#include <filetype.h>
#include <flags.h>
#include <float.h>
#include <time.h>
#include <sfsys.h>
#include <string.h>
#include <math.h>

#define SPACECNT    (32)

static int  try_header(int chans,double inverse_sr,dataptr dz);
static void make_time_display(char temp[],char timestr[],double secs);
static int  get_and_set_float_maxmina(float *flbuf,dataptr dz);
static int  get_and_set_float_maxmin(float *buf,int chans, double inverse_sr,dataptr dz);

static int  compare_sndfile_properties(char temp[],dataptr dz);
static int  compare_analfile_properties(dataptr dz);
static int  read_both_the_files(int samesize,dataptr dz);
static int  read_samps_for_cdiff(dataptr dz);
static void compare_samps(char temp[],int *badmatch,int *limitcnt,double threshold,dataptr dz);
static void anal_file_time(char temp[],int n,dataptr dz);
static void snd_file_time(char temp[],int n,dataptr dz);
static int  sfdiff_process(char temp[],int samesize,dataptr dz);
static int  compare_infile_sizes(int *same_size,dataptr dz);

static int  do_sfdiff(char temp[],dataptr dz);
static int  prntprops(SFPROPS *prop, dataptr dz);
static int  get_float_maxmin(float *buf,int chans, double inverse_sr,dataptr dz);
/*RWD 6:2001 */
static int getpeakdata(int ifd, float *peakval,SFPROPS *props);

static int do_zcross_ratio(dataptr dz);

/****************************** DO_SNDINFO ******************************/

int do_sndinfo(dataptr dz)
{
    int exit_status;
    float *flbuf;
    float *buf;
    double sr = (double)dz->infile->srate;
    int chans = dz->infile->channels, maxpos;
    double other_sr = (double)dz->otherfile->srate;
    int other_chans = dz->otherfile->channels;
    double inverse_sr = 1.0, secs;
    int thisspace, m;
    float *chanmax;
    int ftype = dz->infile->filetype;
    int is_a_text_file = FALSE;
    int n, k, j;
    fileptr fptr = dz->infile;
    char timestr[64];
    char temp[200];
    int holesize = 0, holesamp = 0;
    int maxholesize = 0;
    int maxholesamp = 0;
    double maxholelen, maxholetime;
    double threshold, level = 0.0;
    int orig_ifd0;
    SFPROPS props = {0};
    infileptr ifp;
    double maxamp, maxloc;
    int maxrep;
    int getmax = 0, getmaxinfo = 0;

    if(sr > 0.0)
        inverse_sr = 1.0/sr;
    switch(dz->process) {

    case(INFO_TIMESUM):
        secs = 0.0;
        for(n=0;n<dz->infilecnt;n++) {
            secs += (double)(dz->insams[n]/chans) * inverse_sr;
            secs -= dz->param[TIMESUM_SPLEN] * MS_TO_SECS;
        }
        sprintf(errstr,"TOTAL TIME ");
        make_time_display(temp,timestr,secs);
        strcat(errstr,timestr);
        strcat(errstr,"\n");
        print_outmessage(errstr);
        break;

    case(INFO_TIMELIST):    
        if((ifp = (infileptr)malloc(sizeof(struct filedata)))==NULL) {
            sprintf(errstr,"INSUFFICIENT MEMORY to store data on file.\n");
            return(MEMORY_ERROR);
        }
        secs = (double)(dz->insams[0]/chans) * inverse_sr;
        sprintf(errstr,"%s",dz->wordstor[0]);
        thisspace = SPACECNT - strlen(dz->wordstor[0]);
        thisspace = max(thisspace,2);
        for(m=0;m<thisspace;m++)
            strcat(errstr,".");
        make_time_display(temp,timestr,secs);
        strcat(errstr,timestr);
        strcat(errstr,"\n");
        print_outmessage(errstr);
//TW UPDATE
        if(sloom) {
            sprintf(errstr,"%lf\n",secs);
            if(fputs(errstr,dz->fp)<0) {
                sprintf(errstr,"Cannot output data to file\n");
                return(SYSTEM_ERROR);
            }
        }

        for(n=1;n<dz->infilecnt;n++) {
            if((exit_status = readhead(ifp,dz->ifd[n],dz->wordstor[n],&maxamp,&maxloc,&maxrep,getmax,getmaxinfo))<0) {
                sprintf(errstr,"Cannot re-read file %d header.\n",n+1);
                return(PROGRAM_ERROR);
            }
            copy_to_fileptr(ifp,dz->infile);
            secs = (double)(dz->insams[n]/dz->infile->channels)/(double)dz->infile->srate;
            sprintf(errstr,"%s",dz->wordstor[n]);
            thisspace = SPACECNT - strlen(dz->wordstor[n]);
            thisspace = max(thisspace,2);
            for(m=0;m<thisspace;m++)
                strcat(errstr,".");
            make_time_display(temp,timestr,secs);
            strcat(errstr,timestr);
            strcat(errstr,"\n");
            print_outmessage(errstr);
//TW UPDATE
            if(sloom) {
                sprintf(errstr,"%lf\n",secs);
                if(fputs(errstr,dz->fp)<0) {
                    sprintf(errstr,"Cannot output data to file\n");
                    return(SYSTEM_ERROR);
                }
            }

        }
        break;
    case(INFO_LOUDLIST):    
        orig_ifd0 = dz->ifd[0];
        for(m=0;m<dz->infilecnt;m++) {
            float fmaxamp = 0.0f;       /*RWD 6:2001 */
            int gotpeak = 0;
            /*RWD 6:2001 try to read PEAK chunk too! */
            if(getpeakdata(dz->ifd[m],&fmaxamp,&props)){
                level = fmaxamp;
                gotpeak = 1;
            }
            
            else {
                /* RWD impossible to report int values without lot of hoohaa */
                /* just look as if its floatsam file, for now */
                if(sndgetprop(dz->ifd[m],"fmaxamp",(char *)&fmaxamp,sizeof(float)) < 0 ) {
                    dz->samps_left = dz->insams[m];             
                    dz->ifd[0] = dz->ifd[m];
                    while(dz->samps_left) {
                        if((exit_status = read_samps(dz->bigbuf,dz))<0)
                            return(exit_status);
                        for(n=0;n<dz->ssampsread;n++)
                            fmaxamp = (float)max(fmaxamp,fabs(dz->bigbuf[n]));
                    }
                }
            }
            if(!gotpeak) /*RWD 6:2001 */                
                level = (double)fmaxamp/F_MAXSAMP;

            sprintf(errstr,"%s\t",dz->wordstor[m]);
            sprintf(timestr,"%lf\n",level);
            strcat(errstr,timestr);
            print_outmessage(errstr);
            if(fputs(timestr,dz->fp)<0) {
                sprintf(errstr,"Cannot output data to file\n");
                return(SYSTEM_ERROR);
            }
            dz->ifd[0] = orig_ifd0;
        }
        break;


    case(INFO_TIMEDIFF):
        sprintf(errstr,"DIFFERENCE IS ");
        secs  = (dz->insams[0]/chans) * inverse_sr;
        secs -= (dz->insams[1]/other_chans)/other_sr;
        secs  = fabs(secs);
        make_time_display(temp,timestr,secs);
        strcat(errstr,timestr);
        strcat(errstr,"\n");
        print_outmessage(errstr);
        break;
    
    case(INFO_SFLEN):
        switch(dz->infile->filetype) {
        case(SNDFILE):
            print_outmessage("A soundfile\n");
            sprintf(errstr,"DURATION: ");
            secs = (double)(dz->insams[0]/dz->infile->channels) * inverse_sr;
            make_time_display(temp,timestr,secs);
            strcat(errstr,timestr);
            sprintf(temp,"samples %d\n",dz->insams[0]);
            strcat(errstr,temp);
            break;
        case(ANALFILE):
            print_outmessage("An analysis data file\n");
            sprintf(errstr,"DURATION: ");
            secs = (double)dz->wlength * dz->frametime;
            make_time_display(temp,timestr,secs);
            strcat(errstr,timestr);
            sprintf(temp,"windows %d : floats %d\n",dz->wlength,dz->insams[0]);
            strcat(errstr,temp);
            break;
        case(PITCHFILE):
            print_outmessage("A binary pitch data file\n");
            sprintf(errstr,"DURATION: ");
            secs = (double)dz->insams[0] * dz->frametime;
            make_time_display(temp,timestr,secs);
            strcat(errstr,timestr);
            sprintf(temp,"windows %d : floats %d\n",dz->insams[0],dz->insams[0]);
            strcat(errstr,temp);
            break;
        case(TRANSPOSFILE):
            print_outmessage("A binary transposition data file\n");
            sprintf(errstr,"DURATION: ");
            secs = (double)dz->insams[0] * dz->frametime;
            make_time_display(temp,timestr,secs);
            strcat(errstr,timestr);
            sprintf(temp,"windows %d : floats %d\n",dz->insams[0],dz->insams[0]);
            strcat(errstr,temp);
            break;
        case(ENVFILE):
            print_outmessage("A binary envelope file\n");
            sprintf(errstr,"DURATION: ");
            secs = (double)dz->insams[0] * dz->infile->window_size * MS_TO_SECS;
            make_time_display(temp,timestr,secs);
            strcat(errstr,timestr);
            sprintf(temp,"windows %d : floats %d\n",dz->wlength,dz->insams[0]);
            strcat(errstr,temp);
            break;
        case(FORMANTFILE):
            print_outmessage("A formant data file\n");
            sprintf(errstr,"DURATION: ");
            dz->wlength  = (dz->insams[0]/dz->specenvcnt) - DESCRIPTOR_DATA_BLOKS;
            secs = (double)dz->wlength * dz->frametime;
            make_time_display(temp,timestr,secs);
            strcat(errstr,timestr);
            n = dz->insams[0] - (dz->specenvcnt * DESCRIPTOR_DATA_BLOKS);
            sprintf(temp,"windows %d : floats %d\n",dz->wlength,n);
            strcat(errstr,temp);
            break;
        default:
            sprintf(errstr,"This process only works with soundfiling-system files.\n");
            return(DATA_ERROR);
        }
        print_outmessage(errstr);
        break;

    case(INFO_LOUDCHAN):
        if(dz->infile->channels==MONO) {
            sprintf(errstr,"This program does not works with MONO files.\n");
            return(GOAL_FAILED);
        }
        if((chanmax = (float *)malloc(chans *sizeof(float)))==NULL) {
            sprintf(errstr,"INSUFFICIENT MEMORY to store channel maximi.\n");
            return(MEMORY_ERROR);
        }
        for(m=0;m<chans;m++)
            chanmax[m] = 0;
        while(dz->samps_left) {
            if((exit_status = read_samps(dz->bigbuf,dz))<0)
                return(exit_status);
            for(n=0;n<dz->ssampsread;n+=chans) {
                for(m=0;m<chans;m++)
                    chanmax[m] = (float)max(chanmax[m],(float) fabs(dz->bigbuf[n+m]));
            }
        }
        maxpos = 0;
        for(m=1;m<chans;m++) {
            if(chanmax[m] > chanmax[maxpos])
                maxpos = m;
        }
        sprintf(errstr,"LOUDEST CHANNEL is %d\n",maxpos+1);                         
        print_outmessage(errstr);
        free(chanmax);
        break;

    case(INFO_SAMPTOTIME):
        if(!dz->vflag[CHAN_GROUPED])
            dz->iparam[INFO_SAMPS] /= chans;
        secs = dz->iparam[INFO_SAMPS] * inverse_sr;
        make_time_display(temp,timestr,secs);
        sprintf(errstr,"TIME %s",timestr);
        strcat(errstr,"\n");
        print_outmessage(errstr);
        break;

    case(INFO_TIMETOSAMP):
        n = round(dz->param[INFO_TIME] * sr);
        if(!dz->vflag[CHAN_GROUPED])
            n *= chans;
        sprintf(errstr,"SAMPLE %d\n",n);
        print_outmessage(errstr);
        break;

    case(INFO_PROPS):
        errstr[0] = ENDOFSTR;
        switch(ftype) {
        case(SNDFILE):      
            sprintf(errstr,"A SOUND file.\n");                  break;
        case(ANALFILE): 
            sprintf(errstr,"An ANALYSIS file.\n");              break;
        case(PITCHFILE):
            sprintf(errstr,"A binary PITCH file.\n");           break;
        case(TRANSPOSFILE): 
            sprintf(errstr,"A binary TRANSPOSITION file.\n");   break;
        case(FORMANTFILE):
            sprintf(errstr,"A FORMANT data file.\n");           break;
        case(ENVFILE):  
            sprintf(errstr,"A binary ENVELOPE file.\n");        break;
        default:            
            if(is_a_textfile_type(ftype))    {
                sprintf(errstr,"A TEXT data file.\n");              
                is_a_text_file = TRUE;
            } else {                                
                print_outmessage("UNKNOWN input file type.\n");
                return(FINISHED);
            }
            break;
        }
        print_outmessage(errstr);
        if(is_a_text_file) {
            sprintf(errstr,"Contains %d LINES\n",dz->linecnt);
            print_outmessage(errstr);
            sprintf(errstr,"Contains %d WORDS\n",dz->all_words);
            print_outmessage(errstr);
            if(ftype==MIXFILE) {
                sprintf(errstr,"Could be a MIXFILE with output channel cnt %d\n",dz->out_chans);
                print_outmessage(errstr);
            } else if(ftype==SYNCLIST) {
                sprintf(errstr,"Could be a LIST OF SOUNDFILES TO SYNCHRONISE.\n");
                print_outmessage(errstr);
            } else if(ftype==SNDLIST) {
                sprintf(errstr,"Could be a LIST OF SOUNDFILES.\n");
                print_outmessage(errstr);
            }
            if(dz->tempsize >0 || (dz->extrabrkno >= 0  && dz->brksize[dz->extrabrkno] > 0)) {
                sprintf(errstr,"Could be a BREAKPOINT FILE:\n");
                print_outmessage(errstr);
                sprintf(errstr,"length %d minval %lf maxval %lf  duration %lf\n",
                    dz->numsize/2,dz->minbrk,dz->maxbrk,dz->duration);
                print_outmessage(errstr);
            }
            if(dz->numsize > 0) {
                sprintf(errstr,"Could be a NUMBER LIST:\n");
                print_outmessage(errstr);
                sprintf(errstr,"length %d minval %lf maxval %lf\n",dz->numsize,dz->minnum,dz->maxnum);
                print_outmessage(errstr);
            }
        } else {
            sprintf(errstr,"samples: ............ %d\n",dz->insams[0]);
            print_outmessage(errstr);
            if(ftype!=SNDFILE) {
                switch(ftype) {
                case(ANALFILE): 
                    sprintf(errstr,"file type: .......... ANALYSIS DATA\n");                break;
                case(PITCHFILE):
                    sprintf(errstr,"file type: .......... PITCH DATA (binary)\n");          break;
                case(TRANSPOSFILE): 
                    sprintf(errstr,"file type: .......... TRANSPOSITION DATA (binary)\n");  break;
                case(FORMANTFILE):
                    sprintf(errstr,"file type: .......... FORMANT DATA (binary)\n");        break;
                case(ENVFILE):  
                    sprintf(errstr,"file type: .......... ENVELOPE DATA (binary)\n");       break;
                }
                print_outmessage(errstr);
//TW UPDATE: moved srate & chans, as ENVFILES have srate and channels
                sprintf(errstr,"sample rate: ........ %d\n",fptr->srate);
                print_outmessage(errstr);
                sprintf(errstr,"channels: ........... %d\n",fptr->channels);
                print_outmessage(errstr);
                if(fptr->filetype==ENVFILE) {
                    sprintf(errstr,"window size: ........ %f ms\n",fptr->window_size);
                    print_outmessage(errstr);
                    return(FINISHED);               
                }
                if(fptr->filetype==FORMANTFILE) {
                    sprintf(errstr,"formant envelope cnt: %d\n",fptr->specenvcnt);
                    print_outmessage(errstr);
                }
                /*RWD*/
//TW NOW REDUNDANT: flagging changed
//              if(fptr->filetype!=SNDFILE){
                sprintf(errstr,"original sampsize: .. %d\n",fptr->origstype);
                print_outmessage(errstr);
                sprintf(errstr,"original sample rate: %d\n",fptr->origrate);
                print_outmessage(errstr);
                sprintf(errstr,"analysis rate: ...... %f\n",fptr->arate);
                print_outmessage(errstr);
                sprintf(errstr,"analysis window len:  %d\n",fptr->Mlen);
                print_outmessage(errstr);
                sprintf(errstr,"decimation factor: .. %d\n",fptr->Dfac);
                print_outmessage(errstr);
//              }
                if(fptr->filetype==PITCHFILE || fptr->filetype==TRANSPOSFILE) {
                    sprintf(errstr,"original channels: .. %d\n",fptr->origchans);
                    print_outmessage(errstr);
                }
            } else {
                sprintf(errstr,"file type: ........... SOUND\n");
                print_outmessage(errstr);
                sprintf(errstr,"sample rate: ........ %d\n",fptr->srate);
                print_outmessage(errstr);
                sprintf(errstr,"channels: ........... %d\n",fptr->channels);
                print_outmessage(errstr);
            }
            if((exit_status = prntprops(&props,dz))<0)
                return(exit_status);
        }
        break;
    case(INFO_MAXSAMP):
        if(is_a_text_input_filetype(dz->infile->filetype)) {
            sprintf(errstr,"This is a text file.\n");
            return(GOAL_FAILED);
        }
        /* RWD 2022 this hangs if user supplies anal file */
        /* now fixed by TW 2022 (tlkib3.c) */
        //if(dz->infile->filetype == ANALFILE){
        //    sprintf(errstr,"This is an anlysis file.\n");
        //    return(GOAL_FAILED);
        //}
        flbuf = (float *)dz->bigbuf;
        buf   = dz->bigbuf;
        if(!dz->vflag[FORCE_SCAN] && try_header(chans,inverse_sr,dz)==FINISHED)
            break;
        else {
            if(dz->infile->filetype != SNDFILE)  {
                if((exit_status = get_and_set_float_maxmina(flbuf,dz))<0)
                        return(exit_status);                                
            } else {
                if((exit_status = get_and_set_float_maxmin(buf,chans,inverse_sr,dz))<0)
                    return(exit_status);
            }
        }
        break;
    case(INFO_MAXSAMP2):
        buf   = dz->bigbuf;
        if((exit_status = get_float_maxmin(buf,chans,inverse_sr,dz))<0)
            return(exit_status);
        break;
    
    case(INFO_PRNTSND):
        dz->ssampsread = 0;
        dz->iparam[PRNT_START] = round(dz->param[PRNT_START] * sr) * chans;
        dz->iparam[PRNT_END]   = round(dz->param[PRNT_END] * sr)  * chans;
        if(dz->iparam[PRNT_START] >= dz->iparam[PRNT_END]) {
            sprintf(errstr,"Incompatible start and end times.\n");
            return(DATA_ERROR);
        }
        if(dz->iparam[PRNT_START] >= dz->insams[0]) {
            sprintf(errstr,"Start time is at end of file: can't proceed.\n");
            return(DATA_ERROR);
        }
        
        dz->total_samps_read = 0;
        for(;;) {
            if((exit_status = read_samps(dz->bigbuf,dz))<0)
                return(exit_status);
            if(dz->iparam[PRNT_START] > dz->ssampsread) {
                dz->iparam[PRNT_START] -= dz->ssampsread;
                dz->iparam[PRNT_END]   -= dz->ssampsread;
            } else
                break;
        }
        k = 0;
        j = (dz->total_samps_read - dz->ssampsread + dz->iparam[PRNT_START])/chans;
        display_virtual_time(0L,dz);
        for(n=dz->iparam[PRNT_START];n<dz->iparam[PRNT_END];n+=chans,k+=chans,j++) {
            fprintf(dz->fp,"[%d]\t",j);
            for(m=0;m<chans;m++)
                fprintf(dz->fp,"%.6f\t",(float) dz->bigbuf[n+m]);               /*RWD 12:2003 was %d */
            fprintf(dz->fp,"\n");
            if(n >= dz->ssampsread) {
                if((exit_status = read_samps(dz->bigbuf,dz))<0)
                    return(exit_status);
//TW CHANGED to samps
                display_virtual_time(k,dz);
                dz->iparam[PRNT_END] -= dz->ssampsread;
                n = 0;
            }
        }
//TW CHANGED to samps
        display_virtual_time(k,dz);
        break;
    case(INFO_FINDHOLE):
        threshold = dz->param[HOLE_THRESH] * (double)F_MAXSAMP;
        while(dz->samps_left) {
            if((exit_status = read_samps(dz->bigbuf,dz))<0)
                return(exit_status);
            for(n = 0; n < dz->ssampsread; n++)  {
                if(holesize==0) {
                    if(fabs(dz->bigbuf[n])<threshold) {
                        holesize = 1;
                        holesamp = dz->total_samps_read - dz->ssampsread + n;
                    }
                } else {
                    if(fabs(dz->bigbuf[n])<threshold)
                        holesize++;
                    else {
                        if(holesize > maxholesize) {
                            maxholesize = holesize;
                            maxholesamp = holesamp;
                        }
                        holesize = 0;
                    }
                }
            }
        }
        maxholelen  = (double)(maxholesize/chans) * inverse_sr;
        maxholetime = (double)(maxholesamp/chans) * inverse_sr;
        sprintf(errstr,"Maximum holesize is %.6lf at time %.6lf\n",maxholelen,maxholetime);
        print_outmessage(errstr);
        break;
    case(INFO_CDIFF):
    case(INFO_DIFF):
        if((exit_status = do_sfdiff(temp,dz))<0)
            return(exit_status);
        break;
    case(ZCROSS_RATIO):
        if((exit_status = do_zcross_ratio(dz))<0)
            return(exit_status);
        break;
    default:
        sprintf(errstr,"Unknown process in do_sndinfo()\n");
        return(PROGRAM_ERROR);
    }
    fflush(stdout);
    return(FINISHED);
}

/************************** TRY_HEADER ************************/

int try_header(int chans,double inverse_sr,dataptr dz)
{
    int pos_repeats, neg_repeats, repeats, maxloc, maxamp = -1;
    float maxpfamp, maxnfamp;
    double gain, dbgain;
    float newmaxamp = 0.0f;
    if(dz->infile->filetype != SNDFILE) {
//TW SUBSTITUTED SNDgetprop
        if( sndgetprop(dz->ifd[0],"maxpfamp",(char *)&maxpfamp,sizeof(float)) < 0 )
            return(CONTINUE);
        if( sndgetprop(dz->ifd[0],"maxnfamp",(char *)&maxnfamp,sizeof(float)) < 0 )
            return(CONTINUE);
        if(sndgetprop(dz->ifd[0],"maxprep",(char *)&pos_repeats,sizeof(float)) < 0 )
            return(CONTINUE);
        if(sndgetprop(dz->ifd[0],"maxnrep",(char *)&neg_repeats,sizeof(int)) < 0 )
            return(CONTINUE);
        sprintf(errstr,"maximum float value: %f\n",maxpfamp);
        print_outmessage(errstr);
        sprintf(errstr,"occurs: ............ %d times\n",pos_repeats);
        print_outmessage(errstr);
        sprintf(errstr,"minimum float value: %f\n",maxnfamp);
        print_outmessage(errstr);
        sprintf(errstr,"occurs: ............ %d times\n",neg_repeats);
        print_outmessage(errstr);
    } else {
        if( sndgetprop(dz->ifd[0],"maxamp",(char *)&maxamp,sizeof(int)) < 0         /* old files */
        &&  sndgetprop(dz->ifd[0],"maxpfamp",(char *)&newmaxamp,sizeof(float)) < 0) /* new files */
            return(CONTINUE);
        if( sndgetprop(dz->ifd[0],"maxloc",(char *)&maxloc,sizeof(int)) < 0 )
            return(CONTINUE);
        if( sndgetprop(dz->ifd[0],"maxrep",(char *)&repeats,sizeof(int)) < 0 )
            return(CONTINUE);
        if(maxamp>=0)
            newmaxamp = (float)(maxamp/(double)MAXSAMP);
        sprintf(errstr,"maximum abs value:.. %f\n",newmaxamp);
        print_outmessage(errstr);
        sprintf(errstr,"at: ................ %d samples\n",maxloc);
        print_outmessage(errstr);
        sprintf(errstr,"time: .............. %.4lf secs\n",(double)(maxloc/chans) *inverse_sr);
        print_outmessage(errstr);
        sprintf(errstr,"repeated: .......... %d times\n",repeats);
        print_outmessage(errstr);
        if(newmaxamp>=0.0) {
            gain   = 1.0/(double)maxamp;
            dbgain = log10(gain) * 20.0;
            sprintf(errstr,"max possible gain:.. %.4lf\n",gain);
            print_outmessage(errstr);
            sprintf(errstr,"max dB gain:........ %.4lf\n",dbgain);
            print_outmessage(errstr);
        }
    }
    return(FINISHED);
}

/************************** MAKE_TIME_DISPLAY ************************/

void make_time_display(char temp[],char timestr[],double secs)
{
    int mins = 0, hrs = 0;
    if(secs > 60.0) {
        mins  = round(floor(secs/60.0));
        secs -= (double)(mins * 60);
    }
    if(mins > 60) {
        hrs   = round(floor((double)mins/60.0));
        mins -= hrs * 60;
    }           
    timestr[0] = ENDOFSTR;
    if(hrs) {       
        sprintf(temp,"%d hrs ",hrs);
        strcat(timestr,temp);
    }
    if(mins) {
        sprintf(temp,"%d mins ",mins);
        strcat(timestr,temp);
    }
    sprintf(temp,"%.6lf secs ",secs);
    strcat(timestr,temp);
}

/************************** GET_AND_SET_FLOAT_MAXMIN ************************/
/* RWD assumes analysis ?*/
int get_and_set_float_maxmina(float *flbuf,dataptr dz)
{
    int exit_status;
    double maxpdamp = DBL_MIN;
    double maxndamp = DBL_MAX;
    float maxpfamp, maxnfamp;
    int   maxprep = 1, maxnrep = 1;
    int n;
    dz->samps_left = dz->insams[0];
    if(sndseekEx(dz->ifd[0],0,0)<0) {
        sprintf(errstr,"sndseek() failed. get_and_set_float_maxmin()\n");
        return(SYSTEM_ERROR);
    }
    while(dz->samps_left > 0) {
        if((exit_status = read_samps(flbuf,dz))<0)
            return(exit_status);
        for(n=0;n<dz->ssampsread;n++) {
            if(flbuf[n] < maxpdamp)     /* IF < max, ignore */
                ; 
            else if(flbuf[n] > maxpdamp) {  
                maxpdamp = flbuf[n];    /* IF > max, reset max and count */
                maxprep  = 1;
            } else                      /* else must be EQUAL to max; incr counter */
                maxprep++;
    
            if(flbuf[n] > maxndamp)
                ; 
            else if(flbuf[n] < maxndamp) {
                maxndamp = flbuf[n];
                maxnrep  = 1;
            } else
                maxnrep++;
        }
    }
    maxpfamp = (float)maxpdamp;
    maxnfamp = (float)maxndamp;
//TW cannot reset header of a read-in file
//  if( sndputprop(dz->ifd[0],"maxpfamp",(char *)&maxpfamp,sizeof(float)) < 0 )
//      fprintf(stdout,"WARNING: Cannot write maximum float value to header\n");
//  if(sndputprop(dz->ifd[0],"maxprep",(char *)&maxprep,sizeof(long)) < 0 )
//      fprintf(stdout,"WARNING: Cannot write maxfloat repeats-count to header\n");
//  if( sndputprop(dz->ifd[0],"maxnfamp",(char *)&maxnfamp,sizeof(float)) < 0 )
//      fprintf(stdout,"WARNING: Cannot write mimimum float value to header\n");
//  if(sndputprop(dz->ifd[0],"maxnrep",(char *)&maxnrep,sizeof(long)) < 0 )
//      fprintf(stdout,"WARNING: Cannot write minfloat repeats-count to header\n");

    sprintf(errstr,"maximum float value: %f\n",maxpfamp);
    print_outmessage(errstr);
    sprintf(errstr,"occurs: ...........  %d times\n",maxprep);
    print_outmessage(errstr);
    sprintf(errstr,"minimum float value: %f\n",maxnfamp);
    print_outmessage(errstr);
    sprintf(errstr,"occurs: ...........  %d times\n",maxnrep);
    print_outmessage(errstr);
    return(FINISHED);
}

/************************** GET_AND_SET_SHORT_MAXMIN ************************/

int get_and_set_float_maxmin(float *buf,int chans, double inverse_sr,dataptr dz)
{
    int exit_status;
    /*int maxamp = MINSAMP, mag;*/
    float maxamp = F_MINSAMP, mag;
    int maxloc = 0;
    int maxrep = 1;
    int n;
    double gain = 0.0, dbgain = 0.0;
    while(dz->samps_left) {
        if((exit_status = read_samps(buf,dz))<0)
            return(exit_status);
        for(n=0;n<dz->ssampsread;n++) {
            mag = (float) fabs(buf[n]);
            if(mag > maxamp) {
                maxloc = dz->total_samps_read - dz->ssampsread + n;
                maxamp = mag;
                maxrep = 1;
            } else if(mag==maxamp)
                maxrep++;
        }
    }
//TW Cannot alter heqader of an input file
//  if(sndputprop(dz->ifd[0],"maxamp",(char *)&maxamp,sizeof(float)) < 0 )
//  if(sndputprop(dz->ifd[0],"maxpfamp",(char *)&maxamp,sizeof(float)) < 0 )
//      fprintf(stdout,"WARNING: Cannot write maximum value to header.\n");
//  if(sndputprop(dz->ifd[0],"maxloc",(char *)&maxloc,sizeof(long)) < 0 )
//      fprintf(stdout,"WARNING: Cannot write location of maximum value to header.\n");
//  if(sndputprop(dz->ifd[0],"maxrep",(char *)&maxrep,sizeof(long)) < 0 )
//      fprintf(stdout,"WARNING: Cannot write maximum repeats to header.\n");

    if(maxamp>0.0f) {
        gain   = F_MAXSAMP/(double)maxamp;
        dbgain = log10(gain) * 20.0;
    }
    sprintf(errstr,"maximum abs value:.. %f\n",maxamp);
    print_outmessage(errstr);
    sprintf(errstr,"at: ................ %d samples\n",maxloc);
    print_outmessage(errstr);
    sprintf(errstr,"time: .............. %.4lf secs\n",(double)(maxloc/chans) *inverse_sr);
    print_outmessage(errstr);
    sprintf(errstr,"repeated: .......... %d times\n",maxrep);
    print_outmessage(errstr);
    if(maxamp>0.0f) {
        sprintf(errstr,"max possible gain:.. %.4lf\n",gain);
        print_outmessage(errstr);
        sprintf(errstr,"max dB gain:........ %.4lf\n",dbgain);
        print_outmessage(errstr);
    }
    return(FINISHED);
}

/************************ COMPARE_INFILE_SIZES ***********************/

int compare_infile_sizes(int *samesize,dataptr dz)
{   
    if(dz->insams[0] != dz->insams[1]) {
        print_outmessage_flush("Files are not same size.\n");
        if(!dz->vflag[IGNORE_LENDIFF])
            return(FINISHED);
        else {
            dz->insams[0] = min(dz->insams[0],dz->insams[1]);
            *samesize = FALSE;  
        }
    }
    dz->samps_left = dz->insams[0];
    return(CONTINUE);
}

/************************** COMPARE_SNDFILE_PROPERTIES *********************/

int compare_sndfile_properties(char temp[],dataptr dz)
{
    errstr[0] = ENDOFSTR;
    if(dz->infile->srate != dz->otherfile->srate) {
        sprintf(errstr,"Files don't have same sample rate.\n");
        return(FINISHED);
    }
    if(dz->infile->channels != dz->otherfile->channels) {
        sprintf(errstr,"Files don't have same number of channels. ");
        if(dz->vflag[IGNORE_CHANDIFF]) {
            sprintf(temp,"1st has %d : 2nd has %d\n",dz->infile->channels,dz->otherfile->channels);
            strcat(errstr,temp);
        } else {
            strcat(errstr,"\n");
            return(FINISHED);
        }
        print_outmessage(errstr);
    }
    return(CONTINUE);
}

/*************************** SFDIFF_PROCESS **************************/

int sfdiff_process(char temp[],int samesize,dataptr dz)
{
    int exit_status;
    int limitcnt = 0, badmatch = 0;
    double threshold = dz->param[SFDIFF_THRESH] /* * (double)MAXSAMP*/;

    display_virtual_time(0L,dz);
    if(sndseekEx(dz->ifd[0],0,0)<0) {
        sprintf(errstr,"sndseekEx() problem in sfdiff_process()\n");
        return(SYSTEM_ERROR);
    }
    if(dz->process==INFO_DIFF && sndseekEx(dz->ifd[1],0,0)<0) {
        sprintf(errstr,"sndseekEx() problem 2 in sfdiff_process()\n");
        return(SYSTEM_ERROR);
    }
    while(dz->samps_left > 0) {
        if(dz->process==INFO_CDIFF) {
            if((exit_status = read_samps_for_cdiff(dz))<0)
                return(exit_status);
        } else {
            if((exit_status = read_both_the_files(samesize,dz))<0)
                return(exit_status);
        }
        compare_samps(temp,&badmatch,&limitcnt,threshold,dz);
        display_virtual_time(dz->total_samps_read,dz);
        fflush(stdout);
    }
    if(limitcnt == 0) {
        if(dz->process==INFO_CDIFF)
            sprintf(errstr,"Channels are IDENTICAL");
        else {
            if(samesize)
                sprintf(errstr,"Files are IDENTICAL");
            else
                sprintf(errstr,"Files are otherwise IDENTICAL");
        }
        if(dz->process==INFO_CDIFF) {
            if(dz->param[SFDIFF_THRESH] > 0) {
                sprintf(temp," to within %lf\n",dz->param[SFDIFF_THRESH]);
                strcat(errstr,temp);
            }else 
                strcat(errstr,"\n");
        } else {
            switch(dz->infile->filetype) {
            case(SNDFILE):
                if(dz->param[SFDIFF_THRESH] > 0) {
                    sprintf(temp," to within %lf\n",dz->param[SFDIFF_THRESH]);
                    strcat(errstr,temp);
                } else 
                    strcat(errstr,"\n");
                break;
            default:
                if(dz->param[SFDIFF_THRESH] > 0.0) {
                    sprintf(temp," to within %lf\n",dz->param[SFDIFF_THRESH]);
                    strcat(errstr,temp);
                } else
                    strcat(errstr,"\n");
                break;
            }
        }

    } else if(badmatch) {
        if(limitcnt < dz->iparam[SFDIFF_CNT])
            strcat(errstr," : as far as end of file.\n");
        else
            strcat(errstr," : comparison stopped here.\n");
    }
    return(FINISHED);
}

/************************ READ_BOTH_THE_FILES *******************************/

int read_both_the_files(int samesize,dataptr dz)
{   
    int samps_read2;
    if((dz->ssampsread = fgetfbufEx(dz->sampbuf[0], /*dz->bigbufsize*/dz->buflen,dz->ifd[0],0)) < 0) {
        sprintf(errstr, "Can't read samps from 1st soundfile\n");
        return(SYSTEM_ERROR);
    }
    if((samps_read2 = fgetfbufEx(dz->sampbuf[1], dz->buflen,dz->ifd[1],0)) < 0) {
        sprintf(errstr, "Can't read samps from 2nd soundfile\n");
        return(SYSTEM_ERROR);
    }
    if((dz->ssampsread != samps_read2)) {
        if(samesize) {
            sprintf(errstr, "Anomaly in soundfile read.\n");
            return(PROGRAM_ERROR);
        } else
            dz->ssampsread = min(dz->ssampsread,samps_read2);
    }
    dz->total_samps_read += dz->ssampsread;
    dz->samps_left       -= dz->ssampsread;
    return(FINISHED);
}

/************************ READ_SAMPS_FOR_CDIFF *******************************/

int read_samps_for_cdiff(dataptr dz)
{   
    int n, m;
    if((dz->ssampsread = fgetfbufEx(dz->sampbuf[2], dz->buflen * 2,dz->ifd[0],0)) < 0) {
        sprintf(errstr, "Can't read samples from soundfile\n");
        return(SYSTEM_ERROR);
    }
    
    for(n=0,m=0;m<dz->ssampsread;n++,m+=2) {
        dz->sampbuf[0][n] = dz->sampbuf[2][m];
        dz->sampbuf[1][n] = dz->sampbuf[2][m+1];
    }
    dz->total_samps_read += dz->ssampsread;
    dz->samps_left       -= dz->ssampsread;
    return(FINISHED);
}

/**************************** COMPARE_SAMPS ***************************/

void compare_samps(char temp[],int *badmatch,int *limitcnt,double threshold,dataptr dz)
{   
    int   n;
    int   sampsread = dz->ssampsread;
    switch(dz->infile->filetype) {
    case(SNDFILE):
        if(dz->process==INFO_CDIFF)
            sampsread /= STEREO;
        for(n=0;n<sampsread;n++) {
            if(fabs(dz->sampbuf[0][n]-dz->sampbuf[1][n]) > threshold) { 
                if(*badmatch==0) {
                    sprintf(errstr,"Badmatch from ");
                    snd_file_time(temp,n,dz);
                    *badmatch = 1;
                    if(++(*limitcnt) >= dz->iparam[SFDIFF_CNT]) {
                        dz->samps_left = 0;  /* force calling loop to end */
                        return;
                    }
                }
            } else {
                if(*badmatch) {
                    strcat(errstr," as far as ");
                    snd_file_time(temp,n,dz);
                    strcat(errstr,"\n");
                    print_outmessage(errstr);
                    *badmatch = 0;
                }
            }
        }
        break;
    case(ANALFILE):
        dz->ssampsread = sampsread;
        for(n=0;n<dz->ssampsread;n+=2) {
            if(fabs(dz->flbufptr[0][n]-dz->flbufptr[1][n]) > threshold) {   
                if(*badmatch==0) {
                    sprintf(errstr,"Bad frq match from ");
                    anal_file_time(temp,n,dz);
                    *badmatch = 1;
                    if(++(*limitcnt) >= dz->iparam[SFDIFF_CNT]) {
                        dz->samps_left = 0;
                        return;
                    }
                }
            } else {
                if(*badmatch) {
                    strcat(errstr," as far as ");
                    anal_file_time(temp,n,dz);
                    strcat(errstr,"\n");
                    print_outmessage(errstr);
                    *badmatch = 0;
                }
            }
        }       
        if(*badmatch) {
            strcat(errstr," as far as ");
            anal_file_time(temp,n,dz);
            strcat(errstr,"\n");
            print_outmessage(errstr);
            *badmatch = 0;
        }
        for(n=1;n<dz->ssampsread;n+=2) {
            if(fabs(dz->flbufptr[0][n]-dz->flbufptr[1][n]) > threshold) {   
                if(*badmatch==0) {
                    sprintf(errstr,"Bad amp match from ");
                    anal_file_time(temp,n,dz);
                    *badmatch = 1;
                    if(++(*limitcnt) >= dz->iparam[SFDIFF_CNT]) {
                        dz->samps_left = 0;
                        return;
                    }
                }
            } else {
                if(*badmatch) {
                    strcat(errstr," as far as ");
                    anal_file_time(temp,n,dz);
                    strcat(errstr,"\n");
                    print_outmessage(errstr);
                    *badmatch = 0;
                }
            }
        }       
        if(*badmatch) {
            strcat(errstr," to buffer end ");
            anal_file_time(temp,n,dz);
            strcat(errstr,"\n");
            print_outmessage(errstr);
            *badmatch = 0;
        }
        break;
    default:    /* float files */
        dz->ssampsread = sampsread;
        for(n=0;n<dz->ssampsread;n++) {
            if(fabs(dz->flbufptr[0][n]-dz->flbufptr[1][n]) > threshold) {   
                if(*badmatch==0) {
                    sprintf(errstr,"Badmatch from ");
                    anal_file_time(temp,n,dz);
                    *badmatch = 1;
                    if(++(*limitcnt) >= dz->iparam[SFDIFF_CNT]) {
                        dz->samps_left = 0;
                        return;
                    }
                }
            } else {
                if(*badmatch) {
                    strcat(errstr," as far as ");
                    anal_file_time(temp,n,dz);
                    strcat(errstr,"\n");
                    print_outmessage(errstr);
                    *badmatch = 0;
                }
            }
        }       
        break;
    }
}

/***************************** ANAL_FILE_TIME *************************/

void anal_file_time(char temp[],int n,dataptr dz)
{   
    int this_channel;
    double this_time;
    int  wanted;
    int this_sample = (dz->total_samps_read - dz->ssampsread) + n;
    int this_window;
    switch(dz->infile->filetype) {
    case(ANALFILE):
        wanted = dz->infile->Mlen + 2;
        this_window  = this_sample/wanted;   /* TRUNCATE */
        this_sample  = this_sample%wanted;   /* sample count from window base */
        this_channel = this_sample/2;
        this_time    = (double)this_window/(double)dz->infile->arate;
        sprintf(temp,"  channel %d: window %d: time %.4lf ",this_channel,this_window,this_time);
        break;
    case(PITCHFILE):
    case(TRANSPOSFILE):
        this_window = this_sample;
        this_time   = (double)this_window/(double)dz->infile->arate;
        sprintf(temp,"  window %d: time %.4lf ",this_window,this_time);
        break;
    case(ENVFILE):
        this_window = this_sample;
        this_time   = (double)this_window * dz->infile->window_size * MS_TO_SECS;
        sprintf(temp,"  window %d: time %.4lf ",this_window,this_time);
        break;
    case(FORMANTFILE):
        this_sample -= dz->specenvcnt * DESCRIPTOR_DATA_BLOKS;
        this_window  = this_sample/dz->specenvcnt; /* TRUNCATE */
        this_channel = this_sample%dz->specenvcnt;
        this_time   = (double)this_window/(double)dz->infile->arate;
        sprintf(temp,"  channel %d: window %d: time %.4lf ",this_channel,this_window,this_time);
        break;
    }
    strcat(errstr,temp);
}

/**************************** SND_FILE_TIME ***************************/

void snd_file_time(char temp [],int n,dataptr dz)
{   
    int   this_sample;
    double this_time, sr = (double)dz->infile->srate;
    if(dz->process==INFO_CDIFF) {
        this_sample = ((dz->total_samps_read - dz->ssampsread)/STEREO) + n;
        this_time   = (double)this_sample/sr;
    } else {
        this_sample = (dz->total_samps_read - dz->ssampsread) + n;
        this_time   = (double)this_sample/(sr * (double)dz->infile->channels);
    }
    sprintf(temp,"sample %d: time %.4lf ",this_sample,this_time);
    strcat(errstr,temp);
}

/**************************** COMPARE_ANALFILE_PROPERTIES ***************************/

int compare_analfile_properties(dataptr dz)
{
    errstr[0] = ENDOFSTR;
    switch(dz->infile->filetype) {
    case(ENVFILE):
        if(dz->infile->window_size != dz->otherfile->window_size) {
            sprintf(errstr,"Files do not have same WINDOW_SIZE.\n");
            return(FINISHED);
        }
        break;
    case(FORMANTFILE):
        if(dz->infile->specenvcnt != dz->otherfile->specenvcnt) {
            sprintf(errstr,"Files do not have same FORMANT WINDOW CNT.\n");
            return(FINISHED);
        }
        break;
    case(PITCHFILE):
    case(TRANSPOSFILE):
        if(dz->infile->origchans != dz->otherfile->origchans) {
            sprintf(errstr,"Files do not have same ORIGINAL NO. OF CHANNELS.\n");
            return(FINISHED);
        }
        break;
    }
    if(dz->infile->stype != dz->otherfile->stype) {
        sprintf(errstr,"Files do not have same SAMPLE TYPE.\n");
        return(FINISHED);
    }
    if(dz->infile->srate != dz->otherfile->srate) {
        sprintf(errstr,"Files do not have same SAMPLING RATE.\n");
        return(FINISHED);
    }
    if(dz->infile->channels != dz->otherfile->channels) {
        sprintf(errstr,"Files do not have same NO. OF CHANNELS.\n");
        return(FINISHED);
    }
    if(dz->infile->filetype!=ENVFILE) {
        if(dz->infile->origstype != dz->otherfile->origstype) {
            sprintf(errstr,"Files do not have same ORIGINAL SAMPLE TYPE.\n");
            return(FINISHED);
        }
        if(dz->infile->origrate != dz->otherfile->origrate) {
            sprintf(errstr,"Files do not have same ORIGINAL SAMPLING RATE.\n");
            return(FINISHED);
        }
        if(dz->infile->arate != dz->otherfile->arate) {
            sprintf(errstr,"Files do not have same ANALYSIS RATE.\n");
            return(FINISHED);
        }
        if(dz->infile->Mlen != dz->otherfile->Mlen) {
            sprintf(errstr,"Files do not have same ANALYSIS WINDOW LENGTH.\n");
            return(FINISHED);
        }
        if(dz->infile->Dfac != dz->otherfile->Dfac) {
            sprintf(errstr,"Files do not have same DECIMATION FACTOR.\n");  
            return(FINISHED);   
        }
    }
    return(CONTINUE);
}

/********************************* DO_SFDIFF ***********************************/

int do_sfdiff(char temp[],dataptr dz)
{
    int exit_status;
    int samesize = TRUE;

    switch(dz->process) {
    case(INFO_CDIFF):
        if(dz->infile->channels!=2) {
            sprintf(errstr,"Process only works with STEREO files.\n");
            return(DATA_ERROR);
        }
        break;
    default:
        if(is_a_text_input_filetype(dz->infile->filetype)
        || is_a_text_input_filetype(dz->otherfile->filetype)) {
            sprintf(errstr,"Program only works with soundfiling system files.\n");
            return(DATA_ERROR);
        }
        if(dz->infile->filetype != dz->otherfile->filetype) {
            print_outmessage_flush("Files are not of the same type.\n");
            return(FINISHED);
        }
        if(compare_infile_sizes(&samesize,dz)!=CONTINUE)
            return(FINISHED);

        switch(dz->infile->filetype) {      
        case(SNDFILE):
            if(compare_sndfile_properties(temp,dz)!=CONTINUE) {
                print_outmessage_flush(errstr);
                return(FINISHED);
            }
            break; 
        default:
            if(compare_analfile_properties(dz)!=CONTINUE) {
                print_outmessage_flush(errstr);
                return(FINISHED);
            }
            dz->flbufptr[0] = dz->sampbuf[0];
            dz->flbufptr[1] = dz->sampbuf[1];
            break;
        }
        break; 
    }
    if((exit_status = sfdiff_process(temp,samesize,dz))<0)
        return(exit_status);

    print_outmessage_flush(errstr);
    return(FINISHED);
}

/********************************* PRNTPROPS ***********************************/
/* RWD much adapted to report PEAK data if available */
int prntprops(SFPROPS *props,dataptr dz)
{
    char *prop;
    time_t thistime;
    char *p;
    CHPEAK peaks[2000];
    int chans, peaktime;

    if((prop = (char *)malloc(max(sizeof(unsigned int),sizeof(double))))==NULL) {
        sprintf(errstr,"INSUFFICIENT MEMORY to get properties.\n");
        return(MEMORY_ERROR);
    }

    /*RWD find PEAK chunk as priority */
    
    if(snd_headread(dz->ifd[0],props)){
        if((*props).type == wt_wave) {
            switch(props->samptype){      /*RWD Nov 2003 show sample type now we have so many possibilities! */
            case(SHORT16):
                sprintf(errstr,"sample type:  16bit\n");
                break;
            case(FLOAT32):
                sprintf(errstr,"sample type:  32bit floats\n");
                break;          
            case(INT2424):
                sprintf(errstr,"sample type:  24bit packed\n");
                break;
            case(SHORT8):
                sprintf(errstr,"sample type:  8bit\n");
                break;
            case(INT2432):
                sprintf(errstr,"sample type:  24bit in 32bit frames\n");
                break;
            case(INT_32):
                sprintf(errstr,"sample type:  32bit integer\n");
                break;
            default:
                sprintf(errstr,"sample type unknown (custom format)\n");
                break;
            }
            print_outmessage(errstr);
            chans = (*props).chans; 
            if(sndreadpeaks(dz->ifd[0],chans,peaks,&peaktime)>0){
                int i;
                thistime = (time_t) peaktime;
                sprintf(errstr,"PEAK data at: %s\n",ctime(&thistime));
                print_outmessage(errstr);
                for(i=0;i < chans;i++){
                    sprintf(errstr,"CH %d:\tamp = %.4f (%.2f dB)\tFrame %u\n",
                        i+1,peaks[i].value,20 * log10(peaks[i].value),peaks[i].position); 
                    print_outmessage(errstr);
                }
            }
            else {
                sprintf(errstr,"No PEAK chunk in this file");
                print_outmessage(errstr);
            }
        }
    }

    if(sndgetprop(dz->ifd[0], "date", prop, sizeof(unsigned int))>=0
    || sndgetprop(dz->ifd[0], "DATE", prop, sizeof(unsigned int))>=0) {
        thistime = (time_t) *(int*)prop;
        p = ctime(&thistime);
        
        sprintf(errstr,"Date: %s\n",p);
        print_outmessage(errstr);
    }
    fflush(stdout);
    return(FINISHED);
}       

/************************** GET_SHORT_MAXMIN ************************/

int get_float_maxmin(float *buf,int chans, double inverse_sr,dataptr dz)
{
    int exit_status;
    /*int maxamp = MINSAMP;*/
    float maxamp = F_MINSAMP,mag;
    int maxloc = 0;
    int maxrep = 1;
    int n;
    double gain = 0.0, dbgain = 0.0;
    double convertor = (double)(chans * dz->infile->srate);
    int startsamp = round(dz->param[MAX_STIME] * convertor);
    int endsamp = round(dz->param[MAX_ETIME] * convertor);
    int last_total_samps_read;
    int startsrch, endsrch;
    dz->total_samps_read = 0;   /* REDUNDANT ? */
    while(dz->samps_left) {
        last_total_samps_read = dz->total_samps_read;
        if((exit_status = read_samps(buf,dz))<0)
            return(exit_status);
        if(dz->total_samps_read <= startsamp)
            continue;
        else
            startsrch = max(0,startsamp - last_total_samps_read);
        if(dz->total_samps_read > endsamp) {
            if((endsrch = endsamp - last_total_samps_read) < 0)
                break;
        } else
            endsrch = dz->ssampsread;
        for(n=startsrch;n<endsrch;n++) {
            mag = (float) fabs(buf[n]);
            if(mag > maxamp) {
                maxloc = last_total_samps_read + n;
                maxamp = mag;
                maxrep = 1;
            } else if(mag==maxamp)
                maxrep++;
        }
    }
    if(maxamp>0.0f) {
        gain   = (double)F_MAXSAMP/(double)maxamp;
        dbgain = log10(gain) * 20.0;
    }
    sprintf(errstr,"maximum abs value:.. %f\n",maxamp);
    print_outmessage(errstr);
    sprintf(errstr,"at: ................ %d samples\n",maxloc);
    print_outmessage(errstr);
    sprintf(errstr,"time: .............. %.4lf secs\n",(double)(maxloc/chans) *inverse_sr);
    print_outmessage(errstr);
    sprintf(errstr,"repeated: .......... %d times\n",maxrep);
    print_outmessage(errstr);
    if(maxamp>0.0f) {
        sprintf(errstr,"max possible gain:.. %.4lf\n",gain);
        print_outmessage(errstr);
        sprintf(errstr,"max dB gain:........ %.4lf\n",dbgain);
        print_outmessage(errstr);
    }
    return(FINISHED);
}

/*RWD 6:2001 read PEAK chunk */
int getpeakdata(int ifd, float *peakval,SFPROPS *props)
{
    CHPEAK peaks[16];
    int chans, peaktime;
    float maxamp = 0.0f;
    
    if(snd_headread(ifd,props)){
        chans = (*props).chans; 
        if(sndreadpeaks(ifd,chans,peaks,&peaktime)){
            int i;
            for(i=0;i < chans;i++)
                maxamp = max(maxamp,peaks[i].value);
            *peakval = maxamp;
            return 1;
        }       
    }
    /* sf_headread error; but can't do much about it */
    return 0;
}

/************************** DO_ZCROSS_RATIO ************************/

int do_zcross_ratio(dataptr dz)
{
    int exit_status;
    int chans = dz->infile->channels, *phase, k, m;
    int n, *cnt, cntsum = 0, zc0, zc1, lastzc0, lastzc1, bufstart, bufend, bufskip, skipsamps, totsams, total_samps_processed;
    double zc_ratio;
    float *buf = dz->sampbuf[0];
    double srate = dz->infile->srate;
    if((cnt = (int *)malloc(chans * sizeof(int)))==NULL) {
        sprintf(errstr,"NO MEMORY TO STORE ZERO-CROSSING COUNTERS.\n");
        return(MEMORY_ERROR);
    }
    if((phase = (int *)malloc(chans * sizeof(int)))==NULL) {
        sprintf(errstr,"NO MEMORY TO STORE PHASE MARKERS.\n");
        return(MEMORY_ERROR);
    }
    dz->iparam[ZC_START] = (int)round(dz->param[ZC_START] * srate) * chans;
    bufend               = (int)round(dz->param[ZC_END]   * srate) * chans;
    totsams = bufend - dz->iparam[ZC_START];
    dz->tempsize = totsams;
    bufstart = dz->iparam[ZC_START] % dz->buflen;
    if((bufskip = dz->iparam[ZC_START] / dz->buflen) > 0) {
        skipsamps = bufskip * dz->buflen;
        sndseekEx(dz->ifd[0],skipsamps,0);
        bufend   -= skipsamps;
    }
    if((exit_status = read_samps(buf,dz)) < 0)
        return(exit_status);
    if(dz->ssampsread < chans * 2) {
        sprintf(errstr,"INSUFFICIENT DATA TO COUNT ZERO-CROSSINGS.\n");
        return(DATA_ERROR);
    }
    total_samps_processed = 0;
    for(m=0,n = bufstart;m<chans;m++,n++) {
        if(buf[n] > 0)
            phase[m] = 1;
        else if(buf[n] < 0)
            phase[m] = -1;
        else
            phase[m] = 0;
        cnt[m] = 0;
    }
    k = bufstart + chans;
    while(bufend > dz->buflen)  {
        for(n = k; n < dz->ssampsread;n+=chans) {
            for(m=0;m<chans;m++) {
                switch(phase[m]) {
                case(0):
                    if(buf[n+m] > 0)
                        phase[m] = 1;
                    else if(buf[n+m] < 0)
                        phase[m] = -1;
                    break;
                case(1):
                    if(buf[n+m] < 0) {
                        cnt[m]++;
                        phase[m] = -1;
                    }
                    break;
                case(-1):
                    if(buf[n+m] > 0) {
                        cnt[m]++;
                        phase[m] = 1;
                    }
                    break;
                }
            }
        }
        total_samps_processed += (dz->ssampsread - k);
        display_virtual_time(total_samps_processed,dz);
        if((exit_status = read_samps(buf,dz)) < 0)
            return(exit_status);
        bufend -= dz->buflen;
        k = 0;
    }
    if(bufend > 0) {
        for(n = k; n < bufend;n+=chans) {
            for(m=0;m<chans;m++) {
                switch(phase[m]) {
                case(0):
                    if(buf[n+m] > 0)
                        phase[m] = 1;
                    else if(buf[n+m] < 0)
                        phase[m] = -1;
                    break;
                case(1):
                    if(buf[n+m] < 0) {
                        cnt[m]++;
                        phase[m] = -1;
                    }
                    break;
                case(-1):
                    if(buf[n+m] > 0) {
                        cnt[m]++;
                        phase[m] = 1;
                    }
                    break;
                }
            }
        }
        total_samps_processed += (bufend - k);
        display_virtual_time(total_samps_processed,dz);
    }
    for(m=0;m<chans;m++)
        cntsum += cnt[m];
    zc_ratio = (double)cntsum/(double)totsams;
    zc1 = 32768;
    zc0 = (int)round(zc_ratio * zc1);
    do {
        lastzc0 = zc0;
        lastzc1 = zc1;
        zc1 /= 2;
        zc0 = (int)round(zc_ratio * zc1);
    } while(zc0 > 0 && zc1 >= 64);
    zc0 = lastzc0;
    zc1 = lastzc1;
    while(EVEN(zc0)) {
        zc0 /= 2;
        zc1 /= 2;
    }
    fprintf(stdout,"INFO: Proportion of zero-crossings = %lf or approx %d:%d\n",zc_ratio,zc0,zc1);
    fflush(stdout);
    return FINISHED;
}
