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

    FILE....: TONED.C
    TYPE....: C Module
    AUTHOR..: David Rowe
    DATE....: 19/2/98

    Tone detection module.

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

#include "toned.h"
#include "energ.h"
#include "dft.h"
#include "gen.h"
#include "messages.h"

#include <assert.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>


#define	MD	10	/* maximum number of tone detectors per obj	*/
#define	MAX_ST	20	/* maximum number of states per state machine	*/
#define	TICK	8	/* one time unit for state machines		*/
#define N	160	/* analysis frame size				*/

#define	FMIN	100	/* minimum tone detection frequency		*/
#define	FMAX	3900	/* maximum tone detection frequency		*/
#define	FS	8000	/* sample rate					*/

/* states for glitch detector state machine */

#define	G_LOW		0
#define	G_GOING_HIGH	1
#define	G_HIGH		2
#define	G_GOING_LOW	3


/* structure to hold data for each tone detector */

typedef struct {
   uint16_t	state;    /* current state in cadence state machine	*/
   uint16_t	tone;     /* true if current frame has tone (pair)	*/
   uint16_t	ptone;	  /* true if previous frame had a tone present	*/
   uint16_t 	tone_on;  /* no. frames that tone was on		*/
   uint16_t 	tone_off; /* no. frames that tone was off		*/
   uint16_t	glitch_st;/* glitch detector state 			*/
   uint16_t	glitch_cnt;/* glitch detector counter			*/
   uint16_t	tone_glitch;/* true if tone declared presnt after glitch det */
   uint16_t	time;	  /* used for timing DELAYST states		*/

   uint16_t	nstates;  /* number of cadence states			*/
   uint16_t	tone_id;  /* unique ID number for this tone		*/
   uint16_t	ntones;	  /* number of tones (1 or 2)			*/
   uint16_t	f1;	  /* freq of first tone (bins)			*/
   uint16_t	bw1;	  /* half bandwidth of first tone (bins)	*/
   uint16_t	f2;	  /* freq of first tone (bins)			*/
   uint16_t	bw2;	  /* half bandwidth of second tone (bins)	*/
   int16_t	A1;       /* min amp of 1st tone 0dBm0 = 32767		*/
   int16_t	A2;	  /* min amp of 2nd tone 0dbm0 = 32767		*/
   int16_t	twist;    /* allowable difference in tone powers	*/
			  /* E1/E2 < twist AND E2/E1 < twist		*/
			  /* power ratio in Q5				*/
			  /* 30dB = 32000				*/
			  /* 0dB = 32					*/
   int16_t	snr;	  /* min signal to noise ratio 			*/
			  /* 0dB SNR = 32, 30dB = 32000			*/
			  /* power ratio in Q5				*/
   uint16_t	glitch;	  /* number of frames before transition ok      */

   STRAN	*stran;   /* ptr to cadence state transition table	*/

} DETECT;

/*
   Structure to hold all data for each tone detector object.  There will
   be one tone detector object for each channel.  Each tone detector object
   can contain up to MS tone detectors that process the samples from one
   channel.
*/

struct TD
{
	unsigned short id;	    /* ID for this tone detector object	    */
	unsigned short num_td;	    /* number of tone detectors		    */
	int            inbuf[NDFT]; /* input to tone detector		    */
	unsigned short samples;	    /* number of samples in buffers	    */

	DETECT d[MD];		    /* tone detectors			    */
				    /* callback function called tone detected */
	void (*callback)(uint16_t *mess, void *board);
	void *board;
};


static int logging;                     /* true if we are logging data	*/
static uint16_t logmess[DSP_LTONED_LOG];	/* message containing log info	*/
static unsigned short messcnt;			/* current index of logmess	*/
static unsigned short logid;			/* id of object to log		*/
static unsigned short logtoneid;		/* id of tone to log		*/
static unsigned short logframes;		/* how many frames to log	*/
static unsigned short logcnt;			/* frames logged so far		*/


void check_tone(DETECT *d, COMPF X[], long Et, unsigned short logthis);
int check_cadence(DETECT *d, unsigned short id, unsigned short logthis,
                  void (*callback)(uint16_t mess[],void *board),void *board );


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

	FUNCTION.: toned_open
	AUTHOR...: David Rowe
	DATE.....: 19/2/98

	Initialisation function for tone detector, creates a tone detector
	object.  Should be called once (with a different id argument) for each
	channel.

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

void toned_open(struct TD **tdv, unsigned short id,
		void (*callback)(uint16_t *mess,void *board), void *board)
/* void    **tdv;	tone detector info structure	   */
/* ushort  id;		tone detector object ID		   */
{
	struct TD      *td = malloc(sizeof(struct TD));

	*tdv = td;
	assert(td != NULL);

	td->id = id;
	td->num_td = 0;
	td->samples = 0;
	memset(td->inbuf, 0, sizeof(int)*NDFT);
	td->callback = callback;
	td->board = board;

	logging = 0;
}

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

	FUNCTION.: toned_close
	AUTHOR...: David Rowe
	DATE.....: 19/2/98

	Frees memory associated with a call tone detector object.

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

void toned_close(struct TD *td)
/* void    *tdv;	call progress info structure	*/
{
	DETECT  *d;
	int     i;

	assert(td != NULL);

	/* free state table memory */

	for(i=0; i<td->num_td; i++) {
		d = &td->d[i];
		assert(d->stran != NULL);
		free(d->stran);
	}

	free(td);
}

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

	FUNCTION.: toned_add_tone
	AUTHOR...: David Rowe
	DATE.....: 19/2/98

	Call this function to add a tone detector for a particular tone (pair)
	and cadence.  The tone cadence is described using a state transition
	table comprising an array of STATE structures.  The tone detector
	information is downloaded from the host in the form of a message.

	Returns OK for success.

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

void toned_add_tone(struct TD *td, uint16_t *m)
/* void    *tdv;	call progress info structure			    */
/* uint16_t    *m;		message defining call progress tone state machine   */
{
   int     nstates;
   int     num_td;
   unsigned short  type;
   DETECT  *d;
   float   r;
   unsigned short  f1,bw1,f2,bw2;
   unsigned short  exists,tone_id,ind;
   int     i;

   /* validate arguments and message params */

   assert(td != NULL);
   assert(m[1] == PC_TONED_ST);

   num_td = td->num_td;

   /* check object ID is correct */

   assert(m[2] == td->id);
//   assert(m[2] == (td->id%12));

   /* check num states is within limits */

   nstates = m[3];
   assert((nstates > 0) && (nstates < MAX_ST));

   /* determine if we already have a tone decoder with this ID */

   tone_id = m[4];
   exists = 0;
   ind = 0;
   for(i=0; i<num_td; i++) {
       if (td->d[i].tone_id == tone_id) {
	   exists = 1;
	   ind = i;
       }
   }

   if (exists)
       d = &td->d[ind];		/* if exists replace with new toned */
   else {
       assert(num_td < MD);	/* check that we don't have too many tone detectors */
       d = &td->d[num_td];	/* otherwise new tone decoder */
       td->num_td = ++num_td;	/* increment num tone detector counter */
   }

   /* OK, load params from mess to td structure */

   memcpy(&d->nstates, &m[3], sizeof(short)*12);
   assert((d->ntones==1) || (d->ntones==2));

   /* allocate memory for and copy state table into this context */

   if (exists)
	free(d->stran);
   d->stran = (STRAN*)malloc(sizeof(STRAN)*nstates);
   assert(d->stran);
   memcpy(d->stran, &m[15], sizeof(STRAN)*nstates);

   /* validate call prog state types, and scale */

   for(i=0; i<nstates; i++) {
       type = d->stran[i].type;
       if ((type != TIMER) && (type != RISING) && (type != FALLING) && (type != DELAYST)) {
	   assert(0);
       }
   }

   /* initialise tone detector */

   d->tone = 0;
   d->ptone = 0;
   d->tone_on = 0;
   d->tone_off = 0;
   d->state = 0;
   d->glitch_st = G_LOW;
   d->glitch_cnt = 0;
   d->tone_glitch = 0;
   d->time = 0;

   /* convert tone detection params to fixed point -------------------------*/

   /* params for tones 1 */

   f1 = d->f1;
   bw1 = d->bw1;

   bw1 /= 2;
   assert((f1-bw1) >= FMIN);
   assert((f1+bw1) <= FMAX);
   r = (float)NDFT/FS;
   f1 = floor(f1*r+0.5);
   bw1 = smax(floor(bw1*r+0.5),1);	/* minimum of 3 bins wide total */

   d->f1 = f1;
   d->bw1 = bw1;

   assert(d->A1 <= 0);
   d->A1 = 32767.0*pow(10.0,d->A1/20.0);	  /* 0dbm0 == 32767 */

   /* params for tone 2 */

   if (d->ntones == 2) {
       f2 = d->f2;
       bw2 = d->bw2;

       bw2 /= 2;
       assert((f2-bw2) >= FMIN);
       assert((f2+bw2) <= FMAX);
       f2 = floor(f2*r+0.5);
       bw2 = smax(floor(bw2*r+0.5),1);

       d->f2 = f2;
       d->bw2 = bw2;

       assert(d->A2 <= 0);
       d->A2 = 32767.0*pow(10.0,d->A2/20.0);		/* 0dbm0 == 32767 */
       assert(d->twist <= 20);
       d->twist = 32.0*pow(10.0,(d->twist)/10.0);	/* 30dB == 32767  */
							/* 0dB == 32      */
							/* pwr ratio in Q5*/
   }

   /* param for both tones */

   assert((d->snr >= 0) && (d->snr <= 30));
   d->snr = 32.0*pow(10.0,(d->snr)/10.0);		/* 30dB == 32000  */
							/* 0dB == 32	  */
							/* pwr ratio in Q5*/

   /* end tone detector param fixed point conversion -----------------------*/

}

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

	FUNCTION.: toned_del_tone
	AUTHOR...: Ben Kramer
	DATE.....: 14/10/04

	Call this function to delete a tone detector for a particular tone 
	(pair) and cadence. 

        void    *tdv;	call progress info structure
        uint16_t    *m;	message defining call progress tone state machine

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

void toned_del_tone(struct TD *td, uint16_t *m)
{
	int     num_td;
//	unsigned short  type;
	DETECT  *d;
//	float   r;
//	unsigned short  f1,bw1,f2,bw2;
	unsigned short  exists,tone_id,ind;
	int     i;

	/* validate arguments and message params */

	assert(td != NULL);
	assert(m[1] == PC_TONED_ST_DEL);

	/* check object ID is correct */

//	assert(m[2] == (td->id%12));
	assert(m[2] == (td->id));

	/* determine if we already have a tone decoder with this ID */

	num_td = td->num_td;
	tone_id = m[4];
	exists = 0;
	ind = 0;
	for(i=0; i<num_td; i++) {
		if (td->d[i].tone_id == tone_id) {
			exists = 1;
			ind = i;
		}
	}

	if (exists)
		d = &td->d[ind];		/* if exists replace with new toned */
	else
		return;

	/* OK, load params from mess to td structure */

	memcpy(&d->nstates, &m[3], sizeof(short)*12);
	assert((d->ntones==1) || (d->ntones==2));


	/* allocate memory for and copy state table into this context */

	if (exists)
		free(d->stran);
	d->stran = (STRAN*)malloc(sizeof(STRAN) * d->nstates);
	assert(d->stran);
	memcpy(d->stran, &m[15], sizeof(STRAN) * d->nstates);

	/* validate call prog state types, and scale */

	/*
	for(i=0; i < d->nstates; ++i) {
		type = d->stran[i].type;
		if ((type != TIMER) && (type != RISING) && (type != FALLING) && (type != DELAYST)) {
			assert(0);
		}
	}
	*/

	/* initialise tone detector */

	d->tone = 0;
	d->ptone = 0;
	d->tone_on = 0;
	d->tone_off = 0;
	d->state = 0;
	d->glitch_st = G_LOW;
	d->glitch_cnt = 0;
	d->tone_glitch = 0;
	d->time = 0;

	/* convert tone detection params to fixed point -------------------------*/

	/* params for tones 1 */

//	f1 = d->f1;
//	bw1 = d->bw1;

//	bw1 /= 2;
//	assert((f1-bw1) >= FMIN);
//	assert((f1+bw1) <= FMAX);
//	r = (float)NDFT/FS;
//	f1 = floor(f1*r+0.5);
//	bw1 = smax(floor(bw1*r+0.5),1);	/* minimum of 3 bins wide total */

//	d->f1 = f1;
//	d->bw1 = bw1;

//	assert(d->A1 <= 0);
//	d->A1 = 32767.0*pow(10.0,d->A1/20.0);	  /* 0dbm0 == 32767 */

	/* params for tone 2 */

//	if (d->ntones == 2) {
//		f2 = d->f2;
//		bw2 = d->bw2;
//
//		bw2 /= 2;
//		assert((f2-bw2) >= FMIN);
//		assert((f2+bw2) <= FMAX);
//		f2 = floor(f2*r+0.5);
//		bw2 = smax(floor(bw2*r+0.5),1);
//
//		d->f2 = f2;
//		d->bw2 = bw2;
//
//		assert(d->A2 <= 0);
//		d->A2 = 32767.0*pow(10.0,d->A2/20.0);		/* 0dbm0 == 32767 */
//		assert(d->twist <= 20);
//		d->twist = 32.0*pow(10.0,(d->twist)/10.0);	/* 30dB == 32767  */
//		/* 0dB == 32      */
//		/* pwr ratio in Q5*/
//	}

	/* param for both tones */

//	assert((d->snr >= 0) && (d->snr <= 30));
//	d->snr = 32.0*pow(10.0,(d->snr)/10.0);		/* 30dB == 32000  */
	/* 0dB == 32	  */
	/* pwr ratio in Q5*/

	/* end tone detector param fixed point conversion -----------------------*/

	/* And now disable the thing! */
	d->ntones=0;

}

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

	FUNCTION.: toned_start_logging
	AUTHOR...: David Rowe
	DATE.....: 29/4/98

	Called in reponse to message from host to start logging call progress
	detector data for a specific channel and tone detector.

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

void toned_start_logging(struct TD *td, uint16_t *m)
/* void    *tdv;	call progress info structure		*/
/* uint16_t    *m;		message defining logging		*/
{
	assert(td != NULL);
	assert(m[1] == PC_TONED_DEBUG);
	logid = m[2];
	assert(logid == td->id);
	logtoneid = m[3];
	logframes = m[4];

	logging = 1;
	logcnt = 0;
}

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

	FUNCTION.: toned_analyse
	AUTHOR...: David Rowe
	DATE.....: 19/2/98

	Given a frame of samples, uses the tone detectors to determine
	if any valid tones are present on this channel.

	1. The function first buffers the spech samples until N have been
	received.  These are then concatented with the previous NDFT-N to
	produce a vector of NDFT samples.

	2. This vector is windowed and the DFT is computed.

	3. The DFT ouput vector is then used as the raw input for the various
	tone detectors attached to this channel.  For each tone detector, the
	following steps are performed:

	a) The power in each tone is computed by summing the energy in the
	DFT across the bandwidth of interest.

	b) The tone(s) are compared to the parameters in the DETECT structure
	to determine if the tone (pair) is valid.

	c) If the tone (pair) is valid, then the tone is declared to be ON,
	otherwise the tone is declared to be off for this analysis frame.

	d) The tone on or off information is then passed to the cadence
	detector state machine for this tone (pair).  The state machine
	will eventually produce an event if the tone cadence is valid.

	e) the tone on/off value and the duration of the on/off period acts
	as inputs to the cadence detector state machine.

	4. Step (3) is repeated for the number of tone detectors attached to
	this object.

	5. The size (argument n) is not important, as this function breaks it
	into blocks of size N for the purposes of analysis.

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

void toned_analyse(struct TD *td, short *inp, unsigned short n)
/*  void    *tdv;	tone detector object info structure	*/
/*  short   inp[];	input to tone detector object		*/
/*  int     n;		number of samples in inp[]		*/
{
    unsigned short  tocopy;	/* num samples to copy          */
    COMPF   X[NDFT/2+2];	/* DFT of input frame           */
    int     i;
    long    Et;			/* total energy in frame        */
    unsigned short logthis = 0; /* asserted to log this frame   */

    assert(td != NULL);
    assert(inp != NULL);

    /* break input buffers up into blocks of N samples and process */

    while(n) {

	/* copy a block of samples to internal buffers */

	tocopy = smin(N-td->samples,n);
	for(i=0; i<tocopy; i++)
	    td->inbuf[NDFT-N+td->samples+i] = inp[i];
	(td->samples) += tocopy;
	n -= tocopy;

	/* if internal buffers full, process block */

	if (td->samples == N) {
	    td->samples = 0;

	    memset(X, 0, sizeof(X));
	    dft(td->inbuf, X);

	    /* total energy in DFT is constrained to 2^32-1		*/
	    /* eg consider overload input, one sinusoid at 2^15		*/
	    /* this would give energy of 2^15 squared in DFT		*/
	    /* so no scaling required of energy				*/

	    Et = energ((int*)X, 2*NDFT/2);

	    for(i=0; i<td->num_td; i++) {

		/* init log message if enabled for this object */

		if (logging) {
		    if ((logid == td->id) && (i == logtoneid)) {
			logthis = 1;
			messcnt = 0;
			logmess[messcnt++] = DSP_LTONED_LOG;
			logmess[messcnt++] = DSP_TONED_LOG;
			logmess[messcnt++] = td->id;
			logmess[messcnt++] = logtoneid;
			logmess[messcnt++] = logcnt++;
		    }
		}
		else
		    logthis = 0;

		if (td->d[i].ntones != 0){
			check_tone(&td->d[i], X, Et, logthis);
			check_cadence(&td->d[i], td->id, logthis, td->callback,td->board);
		}

		/* send log message if this channel being logged */

		if (logthis) {

		    /* see if we should switch off logging */

		    if (logcnt == logframes)
			logging = 0;

		    logthis = 0;
		}
	    }

	    /* shift DFT input samples for next block */

	    memcpy(td->inbuf, &td->inbuf[N], sizeof(int)*(NDFT-N));
	}

	/* send log message if this channel being logged */

	if ((logging) && (logid == td->id) && (i == logtoneid))
	    td->callback(logmess,td->board);

    } /* while(n) ... */
}

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

	FUNCTION.: check_tone
	AUTHOR...: David Rowe
	DATE.....: 19/2/98

	Given the DFT of a frame of input samples, this function uses the tone
	param information determine if a valid tone (pair) is present in this
	frame.  This provides input information for the cadence state machine.

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

void check_tone(DETECT *d, COMPF X[], long Et, unsigned short logthis)
/*  DETECT  *d;		tone detector params			*/
/*  COMPF   X[];	DFT of input frame			*/
/*  long    Et;		total energy in frame		        */
/*  unsigned short  logthis;	asserted to log this call		*/
{
    long    E1,E2;	/* energy of first and second tone	*/
    long    T1,T2;	/* energy thresholds			*/
    int     st;		/* start bin for energy sum		*/
    int     tone1;	/* asserted if tone 1 present		*/
    int     tone2;	/* asserted if tone 2 present		*/
    long    sig,noise;	/* signal and noise energy		*/
    int     tone;	/* asserted if tone (pair) present	*/
    int	    t1,t2;	/* first and second half of twist test	*/
    int     twist;	/* asserted if twist test passed	*/
    int	    snr;	/* asserted if SNR test passed		*/

    /* check for tone1 */

    st = d->f1 - d->bw1;
    E1 = energ((int*)&X[st], 2*(2*d->bw1+1) );
    T1 = (long)d->A1 * (long)d->A1;
    tone1 = E1 > T1;

    sig = E1;
    noise = Et - E1;

    tone2 = twist = 0;
    if (d->ntones == 2) {

	/* check for tone2 */

	st = d->f2 - d->bw2;
	E2 = energ((int*)&X[st], 2*(2*d->bw2+1) );
	T2 = (long)d->A2 * (long)d->A2;
	tone2 = E2 > T2;

	/* adjust SNR calculations */

	sig += E2;
	noise -= E2;

	/* determine if twist test passes 			*/
	/*    43.-11          11.5            32.-16		*/

	t1 = (E1>>11) < (long)d->twist*(long)(E2>>16);
	t2 = (E2>>11) < (long)d->twist*(long)(E1>>16);
	twist = t1 && t2;
    }

    /* complete SNR test */

    snr = (sig>>11) > (long)d->snr*(long)(noise>>16);

    /* OK, using all of these tests determine if tone (pair) is present */

    tone = 0;
    if (d->ntones == 1) {
	if (tone1 && snr)
	    tone = 1;
    }
    else {
	if (tone1 && tone2 && snr && twist)
	    tone = 1;
    }

    /* use tone flag to set cadence state machine input variables */

    d->ptone = d->tone;	/* update memory of previous frame	*/
    d->tone = tone;

    /* update logging info */

    if (logthis) {
	logmess[messcnt++] = tone1;
	logmess[messcnt++] = tone2;
	logmess[messcnt++] = twist;
	logmess[messcnt++] = snr;
	logmess[messcnt++] = tone;
    }
}

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

	FUNCTION.: check_cadence
	AUTHOR...: David Rowe
	DATE.....: 19/2/98

	Iterates the cadence state machine for a given tone detector.  Uses
	the tone/ptone input variables determined by check_tone() above as
	inputs to the state machine.  

	Calls callback with a message if a valid tone cadence detected.

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

int check_cadence(DETECT *d, unsigned short id, unsigned short logthis,
                  void (*callback)(uint16_t *mess,void *board),void *board )
/*  DETECT  *d;		tone detector params			*/
/*  ushort  id;		object ID				*/
/*  ushort  logthis;	asserted to log this call		*/
{
    unsigned short  tone;	/* asserted if tone present		*/
    int     rising;	/* asserted on rising edge		*/
    int     falling;	/* asserted on falling edge		*/
    unsigned short  tone_on;	/* frames tone on			*/
    unsigned short  tone_off;	/* frames tone off			*/
    int     state;	/* current state			*/
    int     next_state;	/* next state				*/
    STRAN   *stran;	/* current state transition table	*/
    unsigned short  type;	/* current state transition type	*/
    int     glitch_st;
    int     tone_glitch;
    int     event=0;	/* true if event generated 		*/
    uint16_t    mess[DSP_LTONED];

    /* load d variables into locals for convenience */

    tone = d->tone;
    tone_on = d->tone_on;
    tone_off = d->tone_off;
    tone_glitch = d->tone_glitch;

    /*
      Look for edges with deglitching.  Egdes have to be stable for
      deglitch period before being recognised.
    */

    glitch_st = d->glitch_st;
    next_state = glitch_st;
    rising = falling = 0;

    switch(glitch_st) {
	case G_LOW:
	    if (tone) {
		next_state = G_GOING_HIGH;
		d->glitch_cnt = 0;
	    }
	break;
	case G_GOING_HIGH:
	    if (tone) {
		(d->glitch_cnt)+=(N/TICK);
		if (d->glitch_cnt >= d->glitch) {
		    next_state = G_HIGH;
		    rising = 1;
		    tone_glitch = 1;
		}
	    }
	    else
		next_state = G_LOW;
	break;
	case G_HIGH:
	    if (!tone) {
		next_state = G_GOING_LOW;
		d->glitch_cnt = 0;
	    }
	break;
	case G_GOING_LOW:
	    if (!tone) {
		(d->glitch_cnt)+=(N/TICK);
		if (d->glitch_cnt >= d->glitch) {
		    next_state = G_LOW;
		    falling = 1;
		    tone_glitch = 0;
		}
	    }
	break;
	default:
	    assert(0);
    }
    d->glitch_st = next_state;

    if (rising)
	tone_on = 0;
    if (falling)
	tone_off = 0;
    if (tone_glitch)
	tone_on += N/TICK;
    if (!tone_glitch)
	tone_off += N/TICK;

    /* iterate state machine -----------------------------------------------*/

    state = d->state;
    next_state = state;
    stran = d->stran + state;
    type = stran->type;

    /* timer transition, wait for tfire, rising or falling fails */

    if (type == TIMER) {
	if (tone_on > stran->tfire)
	    next_state = state + 1;
	if (rising || falling)
	    next_state = 0;
    }

    /* rising transition, check tone_on against tmin, tmax */

    if ((type == RISING) && rising) {

	/* if tmax ==0 && tmin == 0 dont check */

	if (stran->tmin && stran->tmax) {
	    if ((tone_off > stran->tmin) && (tone_off < stran->tmax))
		next_state = state + 1;
	    else
		next_state = 0;
	}
	else
	    next_state = state + 1;

    }

    /* falling transition, check tone_off against tmin, tmax */

    if ((type == FALLING) && falling) {

	/* if tmax ==0 && tmin == 0 dont check */

	if (stran->tmin && stran->tmax) {
	    if ((tone_on > stran->tmin) && (tone_on < stran->tmax))
		next_state = state + 1;
	    else
		next_state = 0;
	}
	else
	    next_state = state + 1;
    }

    /* delay state, just wait for tfire */

    if (type == DELAYST) {

	d->time += N/TICK;
	if (d->time >= stran->tfire) {
	    next_state = state + 1;
	    d->time = 0;		/* ready for next time */
	}
    }


    /* if passed last state then fill in message params and set event flag */

    if (next_state == d->nstates) {
	mess[0] = DSP_LTONED;
	mess[1] = DSP_TONED;
	mess[2] = id;
	mess[3] = d->tone_id;
	event = 1;
	next_state = 0;
	callback(mess,board);
    }

    /* update d */

    d->state = next_state;
    d->tone_on = tone_on;
    d->tone_off = tone_off;
    d->tone_glitch = tone_glitch;

    /* update logging info */

    if (logthis) {
	logmess[messcnt++] = tone_on;
	logmess[messcnt++] = tone_off;
	logmess[messcnt++] = rising;
	logmess[messcnt++] = falling;
	if (event)
	    logmess[messcnt++] = d->nstates+1;
	else
	    logmess[messcnt++] = state;
	logmess[messcnt++] = next_state;
    }

    return event;
}

