/*---------------------------------------------------------------------------*\

    This program is used to determine the best tone detector parameters based
    on recordings of actual tones.

    AUTHOR..: David Rowe & Ben Kramer
    DATE....: 20/8/98
    AUTHOR..: Ron Lee
    DATE....: 2/2/07

         Voicetronix Voice Processing Board (VPB) Software

         Copyright (C) 1999-2008 Voicetronix www.voicetronix.com.au

         This library is free software; you can redistribute it and/or
         modify it under the terms of the GNU Lesser General Public
         License version 2.1 as published by the Free Software Foundation.

         This library 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 this library; if not, write to the Free Software
         Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
         MA  02110-1301  USA

\*---------------------------------------------------------------------------*/

#include "vpbapi.h"

#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <sys/stat.h>


// constants used in the TRAIN structure

#define	TRAIN_NMARK	100	// up to 100 candence/freq markers
#define	TRAIN_NCAD	10000	// cadence points between 0 and 20 seconds
#define	TRAIN_NSPEC	256	// spectrum points between 0 and 4 kHz

// return codes

#define	TRAIN_OK				0
#define	TRAIN_NOT_ENOUGH_CADENCE		-2
#define	TRAIN_INVALID_ON_THRESH			-3
#define	TRAIN_INVALID_OFF_THRESH		-4
#define TRAIN_INVALID_CADENCE_WINDOW		-5
#define TRAIN_INVALID_BANDWIDTH			-6
#define TRAIN_INVALID_E_THRESH			-7
#define TRAIN_WAVE_FILE_ERROR			-8
#define	TRAIN_WAVE_FILE_NOT_LINEAR		-9
#define	TRAIN_CANT_ALLOC_BUF			-10
#define	TRAIN_WAVE_READ_ERROR			-11
#define	TRAIN_NSAM_TO_SMALL			-12

struct TRAIN
{
	// inputs
	short	OnThresh;	// level (ref 0dB) to switch on
	short	OffThresh;	// level (ref 0dB) to switch off
	short	CadenceWindow;	// Window (ms) for cadence transition
	short	Bandwidth;	// Bandwidth (Hz) for tone frequencies
	float	eThresh;	// threshold to look for second tone
	short	MagTol;		// tolerance (dB) of min magnitude

	// outputs
	short	Cadence[TRAIN_NCAD];	// cadence (signal energy) samples
	int	CadenceX[TRAIN_NCAD];	// cadence X axis in ms
	short	ncad;			// number of cadence points
	int	CadenceM[TRAIN_NMARK];	// cadence markers in ms
	short	nCadMark;		// number of cadence markers 
	short	Spectrum[TRAIN_NSPEC];	// spectrum in dB, covers 0-4kHz
	int	SpectrumX[TRAIN_NSPEC];	// spectrum x axis in Hz
	int	SpectrumM[TRAIN_NMARK];	// spectrum markers in Hz
	short	SpecMag[TRAIN_NMARK];	// spectrum marker mags in dB0
	short	nSpecMark;		// number of spectrum markers 
	float	r;			// converts Spectrum[] index to frequency in Hz
};

struct COMPLX
{
	float real;
	float imag;
};

enum States { LOOK_FOR_ON, LOOK_FOR_OFF };

#define	N	    80		    // processing block size
#define	NFFT	    (2*TRAIN_NSPEC) // FFT size (=512)
#define L2NFFT	    9               // base 2 log of NFFT
#define	NFFT2	    TRAIN_NSPEC	    // useful constant
#define	FS	    8000	    // sample rate
#define	WIN_OFFSET  1000	    // offset to get FFT window in good signal area
#define	EBUF_MAX    300		    // maximum of 3 seconds of energy samples
#define	AMP_MAX	    32767	    // maximum amplitude
#define	FMIN	    300		    // minimum frequency
#define	FMAX	    3300	    // maximum frequency

#define DEBUG
//#define DEBUGFAKE


// prototype continous tone
static VPB_DETECT continuous = {
	2,		// number of cadence states
	1000,		// tone id, caller must fill in
	0,		// number of tones
	0,		// freq1
	0,		// bandwidth1
	0,		// freq2
	0,		// bandwidth2
	-40,		// level1
	-40,		// level2
	10,		// twist
	10,		// SNR
	40,		// glitchs of 40ms ignored

	{
		{
			VPB_RISING,	// state 0
			0,
			0,
			0,
		},

		{
			VPB_TIMER,	// state 1
			2000,
			0,
			0
		}
	}
};

// prototype cadenced tone
static VPB_DETECT cadenced = {
	3,		// number of cadence states
	1000,		// tone id, filled in by user
	0,		// number of tones
	0,		// freq1
	0,		// bandwidth1
	0,		// freq2
	0,		// bandwidth2
	-20,		// level1
	-20,		// level2
	10,		// twist
	10,		// SNR
	40,		// glitchs of 40ms ignored

	{
		{
			VPB_RISING,	// state 0			
			0,
			0,
			0
		},

		{
			VPB_FALLING,	// state 1			
			0,
			0,		// ton min		
			0		// ton max		
		},

		{
			VPB_RISING,	// state 1			
			0,
			0,		// ton min		
			0		// ton max		
		}
	}
};


/* Discrete Fourier Transform
 * Demonstration code by Emil Mikulic, 2001.
 * http://members.optushome.com.au/darkmoon7/sound/dft/
 *
 * Feel free to do whatever you like with this code.
 * Feel free to credit me.
 *
 * Modified by Ben Kramer to suit our application
 * Date: 10/02/2003
 */
static void dft(COMPLX *data, int samples)
{ //{{{
	COMPLX t[samples/2+1];

	for(int r=0; r <= samples/2; ++r)
	{
		t[r].real = (float)0;
		t[r].imag = (float)0;
		for(int k=0; k < samples; ++k)
		{
			float theta = (float)(r*k) * -2.0 * M_PI /
				(float)samples;

			t[r].real += data[k].real * cos(theta);
			t[r].imag += data[k].real * sin(theta);
		}
	}
	for(int r=0; r <= samples/2; ++r)
	{
		data[r].real = t[r].real;
		data[r].imag = t[r].imag;
	}
} //}}}


//! Determines the frequency of a tone or tone pair from a buffer of audio samples.
static int determine_frequency( TRAIN *train, short *buf )
{ //{{{
	int	i,j;
	COMPLX	x[NFFT];
	float	two_pi = (float)(2.0*acos(-1.0));
	float	h;
	int	mark = 0;
	float	eline;
	float	r = (float)FS/NFFT; train->r = r;
	float	Ewin,Etd;
	int	bmin = (int)floor(FMIN/r+0.5);
	int	bmax = (int)floor(FMAX/r+0.5);

	// extract samples, window, and FFT

	// start is the time of the first cadence * Sample rate
	//  divided by 1000 window offset 
	//  (window offset = to get FFT window in good signal area)
	//  ie the sample to start checking at... lets take 100ms
	//  100*8000/2000=400th sample
//	int start = train->CadenceM[0]*FS/1000+WIN_OFFSET;
	int start = train->CadenceM[0]*FS/1000;
	Ewin = (float)0.0;
	Etd = (float)0.0;

	// Put mangled buf info into Complx array to pass to the FDFT
	// We copy NFFT samples to the Complx array (512)
	for(i=0,j=start; i<NFFT; i++,j++) {
		h = (float)(0.5 - 0.5*cos(two_pi*i/NFFT));
		Ewin += h*h;
		x[i].real = (float)(buf[j]*h);

		#ifdef DEBUGFAKE
		x[i].real = 0;
		x[i].real = cos(two_pi*i*0.125);
		#endif

		x[i].imag = (float)0.0;
		Etd += x[i].real*x[i].real;
	}
	//x[0].real = 1;

	// Call the FFT routine

	#ifdef DEBUG
	printf("before fft....\n");
	for (i=0; i<NFFT; i++){
		//printf("sample %d real: %f imag: %f \n",i,x[i].real,x[i].imag);
	}
	#endif
	dft(x,NFFT);


	#ifdef DEBUG
	printf("after fft....\n");
	for (i=0; i< NFFT2; i++){
		//printf("\t%d\t%f\t%f \n",i,x[i].real,x[i].imag);
	}
	#endif

	#ifdef DEBUGFAKE
	exit(0);
	#endif

	// determine energy of each + freq bin
	float e[NFFT2];
	float totale = (float)0.0;
	for(i=0; i<NFFT2; i++) {
		e[i] = x[i].real*x[i].real + x[i].imag*x[i].imag;
		train->Spectrum[i] = (short)(10.0*log10(e[i]+1.0));
		train->SpectrumX[i] = (short)(i*r);
		totale += e[i];
	}

	// search out the single or dual peaks

	do {

		// Determine global maxima, this is the centre of the freuqency 
		float max = (float)0.0;
		int maxpos = 0;
		for(i=bmin; i<bmax; i++) {
			if (e[i] > max) {
				maxpos = i;
				max = e[i];
			}
		}

		// determine vital statistics of frequency
		float maxf = maxpos*r;
		int start_bin = (int)((maxf - train->Bandwidth/2)/r);
		int end_bin = (int)((maxf + train->Bandwidth/2)/r);

		// check limits of start and end of frequency component
		if (start_bin < 0) {
			start_bin = 0;
			end_bin = (int)((start_bin + train->Bandwidth)/r);
		}
		if (end_bin >= NFFT2) {
			end_bin = NFFT2-1;
			start_bin = (int)((end_bin - train->Bandwidth)/r);
		}

		// Sum total energy in bandwidth about maxima
		// Then zero out this band for next iteration
		eline = (float)1.0;
		for(i=start_bin; i<=end_bin; i++) {
			eline += e[i];
			e[i] = (float)0.0;
		}
		train->SpectrumM[mark] = (short)maxf;

		// determine line magnitude
		float mag = (float)sqrt((4.0*eline)/(NFFT*Ewin));
		float magdB = (float)(20*log10((mag+1.0)/AMP_MAX));
		train->SpecMag[mark++] = (short)(magdB);

		/* If most of the spectrum energy is in the first tone,
		 * then exit the while loop.  Otherwise have another 
		 * iteration as significant energy still exists, which 
		 * indicates the presence of a second tone
		 */

	} while ((mark < 2) && ((eline/totale) < train->eThresh));

	train->nSpecMark = mark;

	return TRAIN_OK;
} //}}}

//! Determines the cadence of a tone from a buffer of audio samples.
static int determine_cadence( TRAIN *train, short *buf, int nsam )
{ //{{{
	int	i,j,k;
	float	x;

	// how many frames of 80 samples can we do ?
	if ( (nsam/N) < TRAIN_NCAD){
		train->ncad = nsam/N;
	}
	else {
		train->ncad =  TRAIN_NCAD;
	}

	assert(train->ncad <= TRAIN_NCAD);

	if (train->ncad < 1)
		return(TRAIN_NSAM_TO_SMALL);

	printf("Using %d frames\n",train->ncad);

	// first determine energy of each N sample frame

	float energy[TRAIN_NCAD];

	for(i=0,j=0; i<nsam; i+=N,j++) {
		energy[j] = (float)0.0;
		for(k=0; k<N; k++) {
			x = buf[i+k];
			energy[j] += pow(x, (float)2.0);
		}

		// convert to dB ref 0dB as overload of single sinewave
		energy[j] = (float)(10.0*log10((energy[j]+1.0)/N) - 87.3);
		train->Cadence[j] = (short)energy[j];
		train->CadenceX[j] = j*(N*1000)/FS;
	}

	// Now look for large energy differences between samples,
	// which indicate transitions from on-off or off-on cadence state
	// start by looking for on transitions
	States state = LOOK_FOR_ON;

	int marks = 0;
	j = 1;
	int t = 0;
	do {
		switch(state) {
		    case LOOK_FOR_ON:
			if (energy[j] > train->OnThresh) {
				state = LOOK_FOR_OFF;
				train->CadenceM[marks++] = t;
				printf("Found cadence on\n");
			}
			break;

		    case LOOK_FOR_OFF:
			if (energy[j] < train->OffThresh) {
				state = LOOK_FOR_ON;
				train->CadenceM[marks++] = t;
				printf("Found cadence off\n");
			}
			break;

		    default:
			assert(0);
		}
		++j;
		t += N*1000/FS;

	} while(j < (train->ncad-1) && marks < TRAIN_NMARK);

	train->nCadMark = marks;

	return TRAIN_OK;
} //}}}


//! Trains a VPB tone detector from a buffer of samples.
//
//! @param tone  The programmable tone detector definition.
//! @param train State parameters controlling the algorithm.
//! @param buf   Buffer of input samples (16 bit linear, 8 kHz).
//! @param nsam  The number of input samples to process.
//!
//! @return @c TRAIN_OK upon success, else a negative error code.
//!
//! The caller must enumerate the tone.
static int do_train( VPB_DETECT *tone, TRAIN *train, short *buf, int nsam )
{ //{{{
	// validate 
	assert(tone != NULL);
	assert(train != NULL);
	assert(buf != NULL);

	if (nsam <= 0)
		return(TRAIN_NSAM_TO_SMALL);
	if (train->OnThresh > 0)
		return (TRAIN_INVALID_ON_THRESH);
	if (train->OffThresh > 0)
		return (TRAIN_INVALID_OFF_THRESH);
	if (train->CadenceWindow <= 0)
		return(TRAIN_INVALID_CADENCE_WINDOW);
	if (train->Bandwidth <= 0)
		return(TRAIN_INVALID_BANDWIDTH);
	if ((train->eThresh <=0) || (train->eThresh > 1.0))
		return(TRAIN_INVALID_E_THRESH);

	// analyse cadence and frequency
	int ret;
	ret = determine_cadence(train, buf, nsam);
	if (ret != TRAIN_OK)
		return ret;
	#ifdef DEBUG
	printf("nCadMark: %d \n",train->nCadMark);
	#endif
	// We now want to find out the freqencies for each cadence
	// But we seem to be only performing a single frequency
	// determination
	if (train->nCadMark) {
		ret = determine_frequency(train, buf);
		if (ret != TRAIN_OK)
			return ret;
	}

	// construct programmable tone using extracted parameters 
	if (train->nCadMark < 1){
		return TRAIN_NOT_ENOUGH_CADENCE;
	}
	else if (train->nCadMark < 2) {
		memcpy(tone, &continuous, sizeof(VPB_DETECT));
		tone->ntones = train->nSpecMark;

		// setup frequencies and bandwidths
		tone->freq1 = train->SpectrumM[0];
		tone->bandwidth1 = train->Bandwidth;
		tone->minlevel1 = train->SpecMag[0]-train->MagTol;
		if (tone->ntones == 2) {
			tone->freq2 = train->SpectrumM[1];
			tone->bandwidth2 = train->Bandwidth;
			tone->minlevel2 = train->SpecMag[1]-train->MagTol;
		}
	} else {
		memcpy(tone, &cadenced, sizeof(VPB_DETECT));
		tone->ntones = train->nSpecMark;

		// setup frequencies and bandwidths
		tone->freq1 = train->SpectrumM[0];
		tone->bandwidth1 = train->Bandwidth;
		tone->minlevel1 = train->SpecMag[0]-train->MagTol;
		if (tone->ntones == 2) {
			tone->freq2 = train->SpectrumM[1];
			tone->bandwidth2 = train->Bandwidth;
			tone->minlevel2 = train->SpecMag[1]-train->MagTol;
		}

		// now set up candences
		/* first rising has no time values, it just kicks off 
		 * the state machine we time the second (falling) and 
		 * third (rising) states 
		 */

		int start_cadence=0;
		if (train->nCadMark >5){
			start_cadence=2;
		}
		int falling = train->CadenceM[start_cadence +1]
				- train->CadenceM[start_cadence +0];
		int rising = train->CadenceM[start_cadence +2]
				- train->CadenceM[start_cadence +1];

		tone->stran[1].tmin = falling - train->CadenceWindow/2;
		tone->stran[1].tmax = falling + train->CadenceWindow/2;
		tone->stran[2].tmin = rising - train->CadenceWindow/2;
		tone->stran[2].tmax = rising + train->CadenceWindow/2;
	}

	return TRAIN_OK;
} //}}}

#if 0
//! Trains a VPB tone detector from a wave file of linear samples.
//
//! @param tone  The programmable tone detector definition.
//! @param train State parameters controlling the algorithm.
//! @param file  The wave file name.
//!
//! @return @c TRAIN_OK upon success, else a negative error code.
//!
//! The caller must enumerate the tone.
static int do_train_from_linear_wave( VPB_DETECT *tone, TRAIN *train, const char *file)
{ //{{{
	void		*wave;
	unsigned long	sizebytes;
	unsigned long	sizeshorts;
	unsigned short	mode;
	int		ret;

	try {
		// open wave file and determine size
		#ifdef DEBUG
		printf("Opening file %s \n",file);
		#endif
		wave_open_read(&wave, file);
		wave_get_size(wave, &sizebytes);
		sizebytes=(2*8000*5);
		sizeshorts = sizebytes/sizeof(short);
		#ifdef DEBUG
		printf("Size shorts %ld \n",sizeshorts);
		#endif

		// check if it is a linear file

		wave_get_mode(wave, &mode);
		if (mode != VPB_LINEAR) {
			vpb_wave_close_read(wave);
			return(TRAIN_WAVE_FILE_NOT_LINEAR);
		}

		// allocate a buffer, read samples, and train

		short *buf = new short[sizeshorts];

		if (buf == NULL) {
			vpb_wave_close_read(wave);
			return(TRAIN_CANT_ALLOC_BUF);
		}

		ret = vpb_wave_read(wave, (char*)buf, sizebytes);
		#ifdef DEBUG
		printf("Read %d bytes from file\n",ret);
		printf("Was trying to get %ld bytes\n",sizebytes);
		#endif
		if (ret != (int)sizebytes) {
			vpb_wave_close_read(wave);
			delete [] buf;
			return(TRAIN_WAVE_READ_ERROR);
		}

		ret = do_train(tone,	train, buf, sizeshorts);

		// clean up and exit
		delete [] buf;
		vpb_wave_close_read(wave);
	}
	catch(const Wobbly&) {
		return TRAIN_WAVE_FILE_ERROR;
	}
	return ret;
} //}}}
#endif


#define	AMP1	32000
#define	AMP2	2000
#define	F1	425
#define	F2	1000
#define	FS	8000

#define PLAIN_ENGLISH 	1
#define ENVIRONMENT 	2
#define CSOURCECODE 	3
#define VOXFILE 	4
#define WAVFILE 	5
#define TONEDEBUG 	6

#define TONE_TOL 25

static void usage(char *argv[])
{ //{{{
	printf("usage:\t%s [<option(s)>] [<filename>]\n",basename(argv[0]));
	printf("options:\n");
	printf("\t Input options\n");
	printf("\t\t-v use vox format file <filename> for input\n");
	printf("\t\t-w use wav format file <filename> for input\n");
	printf("\t\t-s <n> Skip the first n seconds of the sample \n");
	printf("\t\t-t <n> Train using n seconds of the sample\n");
	printf("\t\t(The last 2 options are good for when you do not\n");
	printf("\t\t a clean sample of one tone. Adjust these so that\n");
	printf("\t\t tonetrain trains on only 1 tone.)\n\n");
	printf("\t Output options\n");
	printf("\t\t-p Plain English (default)\n");
	printf("\t\t-e Environment variable format\n");
	printf("\t\t-c C source code snippet\n");
} //}}}

static void print_plaineng(VPB_DETECT *tone)
{ //{{{
	printf("Plain english format result:\n");
	printf("Number of Cadence states:\t %d\n",tone->nstates);
	printf("Unique ID number for this tone:\t %d\n",tone->tone_id);
	printf("Number of tones (1 or 2):\t %d\n",tone->ntones);
	printf("Frequency of first tone(Hz):\t %d\n",tone->freq1);
	printf("Bandwidth of first tone(Hz):\t %d\n",tone->bandwidth1);
	printf("Frequency of second tone(Hz):\t %d\n",tone->freq2);
	printf("Bandwidth of second tone(Hz):\t %d\n",tone->bandwidth2);
	printf("Minimum amplitude of first tone (ref 0dbm0):\t %d\n",tone->minlevel1);
	printf("Minimum amplitude of second tone (ref 0dbm0):\t %d\n",tone->minlevel2);
	printf("Allowable difference in tone powers:\t %d\n",tone->twist);
	printf("Minimum signal to noise ratio to accept tone:\t %d\n",tone->snr);
	printf("Short transitision of glitch ignored (in ms):\t %d\n",tone->glitch);
	printf("\n");
	for(int i=0; i < tone->nstates; ++i) {
		printf("Cadence state %d\n",i);
		printf("\tType (VPB_TIMER, VPB_RISING, or VPB_FALLING): %d \n",
		       tone->stran[i].type);
		printf("\tStran, timer mode only: %d \n",tone->stran[i].tfire);
		printf("\tMinimum tone on/off time (non timer) in ms: %d\n",
		       tone->stran[i].tmin);
		printf("\tMaximum tone on/off time (non timer) in ms: %d\n",
		       tone->stran[i].tmax);
		printf("\n");
	}
} //}}}

static void print_env(VPB_DETECT *tone)
{ //{{{
	int ontime,offtime;
	printf("Environment variable format result:\n");

	// We need to determine the Type of tone
	// from the information in the tone structure

	// Check if we have a continous or pulsed tone
	// First Check the number of Tones
	if(tone->ntones <= 1) {
		// We have a C, P, or P2
		if(tone->nstates == 3) {
			// We have a P2
			/*
			*/
			ontime  = (int)((tone->stran[1].tmax + tone->stran[1].tmin)/2);
			offtime = (int)((tone->stran[2].tmax + tone->stran[2].tmin)/2);
			printf("export VPB_TONE=5,P2,%d,%d,%d,%d\n",
			       tone->freq1, tone->bandwidth1, ontime, offtime);
		}
		else if(tone->stran[1].tmin !=0 || tone->stran[1].tmax != 0){
			// We have a P
			printf("export VPB_TONE=5,P,%d,%d,%d\n",
			       tone->freq1, tone->bandwidth1, tone->stran[1].tfire);
		} else {
			// We have a C
			printf("export VPB_TONE=5,C,%d,%d,%d\n",
			       tone->freq1, tone->bandwidth1, tone->stran[1].tfire);
		}
	} else {
		// We have a P3,P4,P5 etc
		if(tone->stran[1].tfire != 0 && tone->stran[1].tmin == 0
		&& tone->stran[1].tmax == 0 && tone->twist == 20)
		{
			// We have a P3
			printf("export VPB_TONE=5,P3,%d,%d,%d,%d,%d,%d\n",
			       tone->freq1, tone->bandwidth1, tone->stran[1].tfire,
			       tone->freq2, tone->bandwidth2, tone->snr);
		}
		else if(tone->stran[1].tfire != 0 && tone->stran[1].tmin == 0
		     && tone->stran[1].tmax ==0 && tone->twist != 20)
		{
			// We have a P6
			printf("export VPB_TONE=5,P6,%d,%d,%d,%d,%d,%d,%d\n",
			       tone->freq1, tone->bandwidth1, tone->stran[1].tfire,
			       tone->freq2, tone->bandwidth2, tone->snr, tone->twist);
		}
		else if(tone->stran[1].tfire == 0
		     && (tone->stran[1].tmin != 0 || tone->stran[1].tmax != 0))
		{
			// We have a P4
			ontime = (int)(100.0*tone->stran[1].tmax/(100.0 - TONE_TOL))
			       - (int)(100.0*tone->stran[1].tmin/(100.0 + TONE_TOL));
			printf("export VPB_TONE=5,P4,%d,%d,%d,%d,%d,%d\n",
			       tone->freq1, tone->bandwidth1, ontime,
			       tone->freq2, tone->bandwidth2, tone->snr);
		}
		else if( tone->stran[1].tfire == 0
		     && (tone->stran[1].tmin != 0 || tone->stran[1].tmax != 0)
		     && (tone->stran[2].tmin != 0 || tone->stran[2].tmax != 0) )
		{
			// We have a P5
			ontime = (int)(100.0*tone->stran[1].tmax/(100.0 + TONE_TOL))
			       - (int)(100.0*tone->stran[1].tmin/(100.0 - TONE_TOL));
			offtime = (int)(100.0*tone->stran[2].tmax/(100.0 + TONE_TOL))
				- (int)(100.0*tone->stran[2].tmin/(100.0 - TONE_TOL));
			printf("export VPB_TONE=5,P5,%d,%d,%d,%d,%d,%d,%d\n",
			       tone->freq1, tone->bandwidth1, ontime, offtime,
			       tone->freq2, tone->bandwidth2, tone->snr);
		}
		else if(tone->twist != 20 && tone->stran[1].tfire == 0
		     && (tone->stran[1].tmin != 0 || tone->stran[1].tmax != 0))
		{
			// We have a P7
			ontime = (int)(100.0*tone->stran[1].tmax/(100.0 + TONE_TOL))
			       - (int)(100.0*tone->stran[1].tmin/(100.0 - TONE_TOL));
			printf("export VPB_TONE=5,P7,%d,%d,%d,%d,%d,%d,%d\n",
			       tone->freq1, tone->bandwidth1, ontime,
			       tone->freq2, tone->bandwidth2, tone->snr, tone->twist);
		}
		else if( tone->twist != 20 && tone->stran[1].tfire == 0
		     && (tone->stran[1].tmin != 0 || tone->stran[1].tmax != 0)
		     && (tone->stran[2].tmin != 0 || tone->stran[2].tmax != 0) )
		{
			// We have a P8
			ontime = (int)(100.0*tone->stran[1].tmax/(100.0 + TONE_TOL))
			       - (int)(100.0*tone->stran[1].tmin/(100.0 - TONE_TOL));
			offtime = (int)(100.0*tone->stran[2].tmax/(100.0 + TONE_TOL))
				- (int)(100.0*tone->stran[2].tmin/(100.0 - TONE_TOL));
			printf("export VPB_TONE=5,P5,%d,%d,%d,%d,%d,%d,%d,%d\n",
			       tone->freq1, tone->bandwidth1, ontime, offtime,
			       tone->freq2, tone->bandwidth2, tone->snr, tone->twist);
		}
		else {
			printf("I have no idea how to represent that tone"
			       " for use as an Environment Variable !!\n");
		}
	}
} //}}}

static void print_tonedebug(VPB_DETECT *tone)
{ //{{{
	if(tone->stran[1].tmin != 0 || tone->stran[1].tmax != 0){
		// We have a P
		//printf("export VPB_TONE=5,P,%d,%d,%d\n",
		//	 tone->freq1,tone->bandwidth1,tone->stran[1].tfire);
		printf("tonedebug -p %d %d %d ",
		       tone->freq1, tone->bandwidth1, tone->stran[1].tfire);
		printf("\n");
	} else {
		// We have a C
		//printf("export VPB_TONE=5,C,%d,%d,%d\n",
		//	 tone->freq1,tone->bandwidth1,tone->stran[1].tfire);
		printf("tonedebug -c %d %d %d ",
		       tone->freq1, tone->bandwidth1, tone->stran[1].tfire);
		printf("\n");
	}
} //}}}

static void print_csource(VPB_DETECT *tone)
{ //{{{
	printf("C source code format result:\n");
	printf("static VPB_DETECT user_tone = {\n");
	printf("\t%d,\t// number of cadence states\n",tone->nstates);
	printf("\tUSER_TONE,\t// tone id ******** RENAME THIS, eg VPB_DIAL"
	       " or VPB_BUSY etc *********\n");
	printf("\t%d,\t// number of tones\n",tone->ntones);
	printf("\t%d,\t// freq1\n",tone->freq1);
	printf("\t%d,\t// bandwidth1\n",tone->bandwidth1);
	printf("\t%d,\t// freq2\n",tone->freq2);
	printf("\t%d,\t// bandwidth2\n",tone->bandwidth1);
	printf("\t%d,\t// level1 \n",tone->minlevel1);
	printf("\t%d,\t// level2\n",tone->minlevel2);
	printf("\t%d,\t// twist\n",tone->twist);
	printf("\t%d,\t// SNR\n",tone->snr);
	printf("\t%d,\t// glitch duration in ms\n",tone->glitch);
	printf("\t{\n");
	for(int i=0; i < tone->nstates; ++i) {
		printf("\t\t// Cadence %d\n",i);
		printf("\t\t{%d,\t// VPB_TIMER, VPB_RISING, or VPB_FALLING \n",
		       tone->stran[i].type);
		printf("\t\t%d,\t// timer mode only \n", tone->stran[i].tfire);
		printf("\t\t%d,\t// minimum tone on/off time (non timer) in ms\n",
		       tone->stran[i].tmin);
		printf("\t\t%d},\t// maximum tone on/off time (non timer) in ms\n",
		       tone->stran[i].tmax);
	}
	printf("\t}\n");
	printf("};\n");
} //}}}

static void print_error(int ecode)
{ //{{{
	switch (ecode) {
	    case TRAIN_OK:
		printf("All ok!\n");
		break;
	    case TRAIN_NOT_ENOUGH_CADENCE:
		printf("Not enough cadence!\n");
		break;
	    case TRAIN_INVALID_ON_THRESH:
		printf("Invalid on-threshold.\n");
		break;
	    case TRAIN_INVALID_OFF_THRESH:
		printf("Invalid off-threshold.\n");
		break;
	    case TRAIN_INVALID_CADENCE_WINDOW:
		printf("Invalid Cadence window\n");
		break;
	    case TRAIN_INVALID_BANDWIDTH	:
		printf("Invalid bandwith\n");
		break;
	    case TRAIN_INVALID_E_THRESH:
		printf("Invalid E threshold\n");
		break;
	    case TRAIN_WAVE_FILE_ERROR:
		printf("Wave file error!\n");
		break;
	    case TRAIN_WAVE_FILE_NOT_LINEAR:
		printf("Wave file not linear!\n");
		break;
	    case TRAIN_CANT_ALLOC_BUF:
		printf("Cant allocate memeory for the buffer.\n");
		break;
	    case TRAIN_WAVE_READ_ERROR:
		printf("Wave read error.\n");
		break;
	    case TRAIN_NSAM_TO_SMALL:
		printf("Number of samples to small.\n");
		break;

	    default:
		printf("Sorry cant translate error # %d\n",ecode);
	}
} //}}}

int main(int argc, char *argv[])
{ //{{{
	int		ecode = TRAIN_WAVE_FILE_ERROR;
	int             comoin, comoout, noisy=0;
	int 		skip=1;
	int		tsecs=3;
	WFILE		*wave;
	FILE		*f;
	TRAIN		train;
	VPB_DETECT	tone;
	struct stat	fileinfo;

	train.OnThresh      = -30;
	train.OffThresh     = -35;
	train.CadenceWindow = 100;
	train.Bandwidth     = 100;
	train.eThresh       = 0.9;
	train.MagTol        = 10;

	if (argc < 2){
		usage(argv);
		return -1;
	}

	// Setup default options
	comoin = WAVFILE;
	comoout = PLAIN_ENGLISH;

	// We should be checking the file size for length of sample to take!
	if(stat(argv[argc-1],&fileinfo) == -1){
		printf("Could not stat file: %s\n",argv[argc-1]);
		exit(0);
	}
	//printf("File length [%d] bytes\n",fileinfo.st_size);
	//printf("File length [%d] seconds\n",fileinfo.st_size/(FS*sizeof(short)));
	tsecs = fileinfo.st_size/(FS*sizeof(short));
	if (tsecs >5){
		skip  = tsecs/3;
		tsecs = skip;
	} else {
		skip  = 0;
		tsecs = 3;
	}

	// parse arguments
//	printf("Parse args\n");
	for(int i=1; i < (argc-1); ++i)
	{
		if (strcmp(argv[i],"-s")==0){
			skip = atoi(argv[i+1]);
			i++;
		}
		else if (strcmp(argv[i],"-t")==0){
			tsecs = atoi(argv[i+1]);
			i++;
		}
		else if (strcmp(argv[i],"-v")==0){
			comoin = VOXFILE;
		}
		else if (strcmp(argv[i],"-w")==0){
			comoin = WAVFILE;
		}
		else if (strcmp(argv[i],"-p")==0){
			comoout = PLAIN_ENGLISH;
		}
		else if (strcmp(argv[i],"-e")==0){
			comoout = ENVIRONMENT;
		}
		else if (strcmp(argv[i],"-c")==0){
			comoout = CSOURCECODE;
		}
		else if (strcmp(argv[i],"-V")==0){
			noisy=1;
		}
		else {
			usage(argv);
			return -1;
		}
	}

	int     varN = tsecs * FS;
	short   buf[varN];

	memset( buf, 0, varN * sizeof(short) );

	/*
	comoin = WAVFILE;
	comoout = PLAIN_ENGLISH;
	printf("Finished parse args\n");
	printf("input => %d\n",comoin);
	printf("output => %d\n",comoout);
	*/

	// process the file
	switch (comoin){
	    case VOXFILE:
	    {
		if(noisy) printf("Processing VOXFILE: %s\n",argv[argc-1]);

		// open it, skipping the first couple of seconds
		// as that might contain junk like clicks etc
		f = fopen(argv[argc-1],"rt");
		fseek(f, (FS*skip)*sizeof(short), SEEK_SET);
		assert(f != NULL);
		fread(buf,sizeof(short),varN,f);
		fclose(f);

		ecode = do_train(&tone, &train, buf, varN);
		break;
	    }

	    case WAVFILE:
	    {
		if(noisy) printf("Processing WAVFILE: %s\n",argv[argc-1]);

		// open wave file and check if it is a linear file
		vpb_wave_open_read(&wave, argv[argc-1]);
		int mode = vpb_wave_get_mode(wave);

		if(mode != VPB_LINEAR) {
			vpb_wave_close_read(wave);
			ecode = TRAIN_WAVE_FILE_NOT_LINEAR;
			break;
		}
		vpb_wave_close_read(wave);

		// open it, skipping the first couple of seconds
		// as that might contain junk like clicks etc
		f = fopen(argv[argc-1],"rt");
		fseek(f, (FS*skip)*sizeof(short), SEEK_SET);
		assert(f != NULL);
		fread(buf,sizeof(short),varN,f);
		fclose(f);

		ecode = do_train(&tone, &train, buf, varN);
		break;
	    }

	    default:
		break;
	}

	if(noisy) {
		printf("The sample had %d cadence states...\n",tone.nstates);
		for(int i=0; i < tone.nstates; ++i){
			printf("\tCadence %d at %d ms \n",i,train.CadenceM[i]);
		}
		printf("Found %d tones...\n",tone.ntones);
	}

	// print the results
	if (ecode == TRAIN_OK){
		switch(comoout) {
		    case PLAIN_ENGLISH:
			print_plaineng(&tone);
			break;

		    case ENVIRONMENT:
			print_env(&tone);
			break;

		    case CSOURCECODE:
			print_csource(&tone);
			break;

		    case TONEDEBUG:
			print_tonedebug(&tone);
			break;

		    default:
			printf("Defaulting to environment\n");
			print_env(&tone);
		}
	} else {
		printf("An error has occured!!\n");
		print_error(ecode);
	}

	return 0;
} //}}}

