Mercurial > hg > aim92
diff model/image.c @ 0:5242703e91d3 tip
Initial checkin for AIM92 aimR8.2 (last updated May 1997).
author | tomwalters |
---|---|
date | Fri, 20 May 2011 15:19:45 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/model/image.c Fri May 20 15:19:45 2011 +0100 @@ -0,0 +1,1271 @@ +/* + SCCS VERSION 1.29 WITH 1.28 STRIPPED OUT. + + + + Copyright (c) Applied Psychology Unit, Medical Research Council. 1988, 1989 + =========================================================================== + + Permission to use, copy, modify, and distribute this software without fee + is hereby granted for research purposes, provided that this copyright + notice appears in all copies and in all supporting documentation, and that + the software is not redistributed for any fee (except for a nominal shipping + charge). Anyone wanting to incorporate all or part of this software in a + commercial product must obtain a license from the Medical Research Council. + + The MRC makes no representations about the suitability of this + software for any purpose. It is provided "as is" without express or implied + warranty. +*/ + +/*************************************************************************** +* image.c Version with trigger at right end of sai. +* ======= +* Input data derived from a cochleagram is used to construct a stabilised +* auditory image (sai) of size `chans' * `framewidth' points. +* +* Organisation of data. +* Data is 2-dimensional: in time (rows) and frequency (columns). +* +* Input data is ordered by columns. The input array consists of blocks (columns) +* of `chans' points, each column being one time sample of `chans' channels. +* The zero'th point in each column is the lowest frequency channel. +* +* Output (sai) data is ordered by rows. The output array consists of blocks +* (rows) of `framewidth' points. +* The zero'th point in each row is the "oldest" time, as the time origin is +* on the right edge of the sai. +* +* Each sai is called a `frame', and its dimensions are `chans' rows by +* `framewidth' columns. In the code, the `framewidth' is often represented +* as imagewidth/chans, where imagewidth is the total amount of data in an +* sai frame, (ie. imagewidth=chans*framewidth). + +* This module has been modified to have 5 levels of strobing. + A Jay Datta 6/03/95 +***************************************************************************/ + +#include <math.h> +#include <string.h> + +#include "options.h" +#include "stitch.h" +#include "source.h" +#include "model.h" +#include "units.h" +#include "image.h" +#include "calc.h" + +#define MAX_BUFFER (1l<<15) + +#ifdef FLOAT +#define INPUT_SCALE 0.25 +#define DECAY_SCALE 0.875 +#else +#define INPUT_SHIFT 2 +#define DECAY_ROUND 7 +#endif +#define DECAY_SHIFT 3 + +#define MAXCHAN 1000 + +#ifndef lint +static char *sccs_id = "@(#)image.c 1.27 J. Holdsworth, M. Allerhand (MRC-APU) 6/6/91" ; +#endif + +#ifdef DSP32 +static int segsize = 16 ; +#else +static int segsize = 50 ; +#endif + +typedef enum { False, True } Bool ; +typedef struct Node{int index; short val; struct Node *next;} node; + +static void doColumn(); +static Pointer sai_callback(); +static Pointer summary_callback(); +static void save_callback(); +static void addIn(); +static void decayImage(); + +void output_simple_strobe_info(); +void doList_strobe_info(); +void output_thresh_info(); +void decay_strobe_threshold(); +void print_trigger_debugging_info(); + +void initlist(); +node *getnode(); +node *insertl(); +void inss(); + +FILE *trigger_file; +FILE *infotxt_file; +FILE *thresho_file; + +int locmax_searchtime[MAXCHAN]; +int locmax_searchstart[MAXCHAN]; +int val[MAXCHAN]; +int ltime[MAXCHAN]; +int initial_strobe_candidate_time[MAXCHAN]; +int ttime[MAXCHAN]; + +static node *start, *endl, *pp, *qq; +static char *exposwitch; + +#ifdef FLOAT + float *S; +#else + short *S; +#endif + + +/*************************************************************************** +* struct _sai_state: +* The source->info->state structure which is initialised by the +* routine Sai, and is then the state argument for the callback +* function: sai_callback. +***************************************************************************/ +struct _sai_state { + struct _pullable_source parent ; + Source source; + void (*trigger)(); /* trigger algorithm (originally doColumn) */ + unsigned chans; /* number of filter channels */ + unsigned framestep; /* sai display update period */ + DataType *image; /* sai array (DataType = short) */ + unsigned imagewidth; /* length of sai array (framewidth*chans) */ + unsigned imagenwidth; /* portion of sai array for transient info */ + unsigned decay_time; /* a factor of sai decay time constant */ + unsigned decay_count; + ScalarType *framedecay; /* array of factors for cochleagram frame */ + int *cps; /* array of 1/centre-freq (in samples) */ + int *isPulse; /* array of bools, to see if data > thres */ + + int Stcrit; /* int restriction */ + int SusThresh; /* Level 2 user settable thresh */ + ScalarType triggerdecay; /* prop/point decay for strobe threshold */ + int *trigtime; /* time of last trigger pulse */ + int *trigheight; /* height of last trigger pulse */ + int *isStrobe; /* array of booleans, set when pulse peak found */ + float *thresh; /* array of thresholds, one per channel */ + float *tlim; /* linear decay value */ + int time; /* keep track of sai */ + int *previnput; /* lock till new pulse found */ + int *def_strobe_candidate_time; + int *def_strobe_height; + char *switch_info; + int tlim1; + int stlag; /* strobe nwid first attempt to decouple nwids */ +}; + +/*************************************************************************** +* Sai: +* Sai is called from SaiEntry (in module model.c). +* Routines in this module it uses are: +* doColumn, (which uses: addIn) +* sai_callback, (which uses: decayImage) +* +* SaiEntry (in model.c) is the entry-point function to the stabilised image +* processing module. Its purpose is to prepare the arguments for Sai by +* converting parameter-option strings to integers, and then to call Sai. +* +* Sai is called with the following arguments (in model.c): +* Sai( source, Frameheight(), segment, width, +* (int) Samples( decaystr )/Framestep(), (int) Freq( samplestr ), +* frequencies, Samples(ttstr)/Framestep(), Samples(cgmstr)/Framestep(), +* (int) Freq("250Hz"), (int) Freq("20Hz") ) ; +* +* The arguments are as follows: +* (The option name refers to the description given by "help". The values etc +* can be found in table.c as described in model.docs). +* +* Argument Option name Default Comment +* -------- ----------- ------- ------- +* chans (see below) Number of filter channels +* framestep segment_sai 16ms SAI display update period +* framewidth duration_sai 32ms SAI duration (ms) +* decay decayrate_sai 32ms SAI decay time constant (SU/ms) 15ms NOW Default +* samplerate samplerate 20000. Input wave sample rate (Hz) +* cfs (see below) An array of filter centre frequencies +* cgmdecay napdecay_sai 16ms NAP (ie cochleagram) decay time constant +* +* Both "chans" and "cfs" are setup from the routine "updateFrequencies" in +* model.c, because both arguments are derived from three basic parameters: +* +* minstr mincf_afb 220Hz Minimum center frequency (Hz) +* maxstr maxcf_afb 4400Hz Maximum center frequency (Hz) +* denstr dencf_afb 4. Filter density (filters/critical band) +* +* The "chans" argument is calculated in the routine "NumberCenterFrequencies", +* and the result is copied into the "frameheightstr" by a call to the routine +* "setFrameheight". +* The "cfs" argument is calculated in the routine "GenerateCenterFrequencies". +* (Both CenterFrequency routines are in gamma_tone.c). +* +***************************************************************************/ + +Source Sai(source, chans, framestep, pwidth, nwidth, decay, samplerate, cfs, ttdecay, cgmdecay, tlim1, tlim2, suslevel, susthresh, switchinfo, expoSwitch,stlag) +Source source ; +int chans, framestep, pwidth, nwidth, decay ; +int samplerate; +double *cfs ; +double ttdecay, cgmdecay ; +int tlim1, tlim2; +int suslevel; +char *susthresh; +char *switchinfo; +char *expoSwitch; +int stlag; +{ + + DeclareNew( struct _sai_state *, state ) ; + int i, chan, startup, segment, framewidth ; + double pts_per_ms, lin_napdec, lin_napfac ; /* roy 11-8-92 roy */ + char *ThreshoFile, *InfotxtFile, *TriggerFile; + + exposwitch=(char *)calloc(5, sizeof(char)); + exposwitch=expoSwitch; + + ThreshoFile = (char *)calloc(256, sizeof(char)); + InfotxtFile = (char *)calloc(256, sizeof(char)); + TriggerFile = (char *)calloc(256, sizeof(char)); + + state->switch_info = (char *)calloc(256,sizeof(char)); + state->switch_info = switchinfo; + + if (!strcmp(susthresh, "on")) + state->SusThresh=0; + else if (!strcmp(susthresh, "off")) + state->SusThresh=1; + else + state->SusThresh=atoi(susthresh); + + if (!strcmp(state->switch_info, "off")) + ; + else if (!strcmp(state->switch_info, "on")) + infotxt_file=stdout; + else{ + strcpy(InfotxtFile, switchinfo); + strcat(InfotxtFile, ".info"); + infotxt_file=fopen(InfotxtFile, "w"); + + strcpy(TriggerFile, switchinfo); + strcat(TriggerFile, ".trigger"); + trigger_file=fopen(TriggerFile, "w"); + + strcpy(ThreshoFile, switchinfo); + strcat(ThreshoFile, ".thresh"); + thresho_file=fopen(ThreshoFile, "w"); + } + + initlist(); + state->tlim1=0; + + framewidth = pwidth+nwidth; + + pts_per_ms = (double)samplerate/1000 ; + + state->stlag=stlag; /* NEW line 95April */ + /* Initialise members of the sai state structure */ + state->source = NewRetainingSource( source, sizeof ( DataType ) * framewidth * chans ) ; + state->chans = chans ; + state->trigger = doColumn ; + state->framestep = framestep * chans ; + state->imagewidth = framewidth * chans ; /* size of sai buffer */ + state->imagenwidth = nwidth * chans ; /* size of buffer for transient info */ + state->decay_time = decay * -log( 1. - 1. / ( 1 << DECAY_SHIFT ) ) ; + state->decay_count = state->decay_time ; + + state->Stcrit = suslevel; + if (stlag==0 && state->Stcrit==5) /* was nwid NEW */ + state->Stcrit = suslevel-1; + + /* state->SusThresh = susthresh; */ + + if (!strcmp(exposwitch, "on")) fprintf(stderr, "Exponential trigger decay SWITCH is ON\n"); +/* if (!strcmp(exposwitch, "off")) printf("SWITCH is OFF\n"); */ + + if (!strcmp(exposwitch, "on")) + state->triggerdecay = (double) 1-((ttdecay / pts_per_ms ) /100.0) ; + else + state->triggerdecay = (double) (ttdecay /pts_per_ms) / 100.0 ; + + if (!strcmp(switchinfo, "off")) + ; + else + fprintf(infotxt_file, "\ntriggerdecay = %15e\n", state->triggerdecay); + + state->isStrobe = NewZeroedArray( int, state->chans, "image.c for Strobe" ) ; + state->isPulse = NewZeroedArray( int, state->chans, "image.c for trigger pulse" ) ; + state->trigtime = NewZeroedArray( int, state->chans, "image.c for trigger time" ) ; + state->trigheight = NewZeroedArray( int, state->chans, "image.c for trigger height" ) ; + state->thresh = NewZeroedArray( float, state->chans, "image.c for thresh" ) ; + state->tlim = NewZeroedArray( float, state->chans, "image.c for decay" ) ; + state->time = 0; /* Time initialized to Zero */ + state->previnput = NewZeroedArray( int, state->chans, "image.c for pulse lock" ) ; + + state->def_strobe_candidate_time = NewZeroedArray( int, state->chans, "image.c for prevtrig time" ); + state->def_strobe_height = NewZeroedArray( int, state->chans, "image.c for prev trig" ); + + for (chan=0; chan < MAXCHAN; chan++) + locmax_searchtime[chan]=0; + for (chan=0; chan < MAXCHAN; chan++) + locmax_searchstart[chan]=0; + for (chan=0; chan < MAXCHAN; chan++) + val[chan]=0; + for (chan=0; chan < MAXCHAN; chan++) + initial_strobe_candidate_time[chan]=0; + for (chan=0; chan < MAXCHAN; chan++) + ltime[chan]=0; + for (chan=0; chan < MAXCHAN; chan++) + ttime[chan]=0; + +#ifdef FLOAT + S=(float *)calloc(state->chans, sizeof(float)); +#else + S=(short *)calloc(state->chans, sizeof(short)); +#endif + + /* Declare new arrays for members of state structure */ + + state->framedecay = NewArray( ScalarType, framewidth, "image.c for decay" ) ; + state->image = NewZeroedArray( DataType, state->imagewidth, "image.c for image" ) ; + state->cps = NewZeroedArray( int, state->chans, "image.c for channel centre periods" ) ; + + /* Initialise cochleagram frame decay factors */ + if (cgmdecay > 0) + { + /* 11111 roy 11-8-92 These mods change to a linear nap decay. 1111111 roy */ + /* It requires pts_per_ms, lin_napdec and lin_napfac declared just after DeclareNew above */ + + lin_napdec = ((cgmdecay/100)/pts_per_ms)/pts_per_ms ; /* roy 19-8 roy */ + + /* cgmdecay is div by pts_per_ms to get back to napdecay in the original units */ + /* When napdecay is interpreted as the % to decay per ms, and since we are */ + /* operating in points, we need napdec/pts_per_ms, as the dec for each pt within the ms */ + /* The decay vector is set to scale the NAP by 1.0 at strobe point. So, in the sai, */ + /* it falls to the left AND RISES to the right of 0 ms. **** roy **** */ + + lin_napfac = 1.0 + nwidth*lin_napdec ; + + for (i=0 ; i < framewidth ; i++) + { + if ( lin_napfac > 0 ) + state->framedecay[i] = SCALAR( lin_napfac ); + else + state->framedecay[i] = SCALAR( 0 ); + lin_napfac -= lin_napdec ; + } + } + else /* disable decay factor on zero argument */ + for (i=0 ; i < framewidth ; i++) + state->framedecay[i] = SCALAR( 1.0 ) ; + + /* Initialise centre-period for each channel */ + for (i=0 ; i < state->chans ; i++) + state->cps[i] = samplerate / cfs[i]; + +#if defined( PC ) || defined( THINK_C ) + if( (long) ( MAX_BUFFER - (long) state->imagewidth * sizeof ( DataType ) ) < (long) ( state->chans * sizeof ( DataType ) ) ) + stitch_error( "Sorry, image larger than maximum buffer size\n" ) ; +#endif + +/* for (i=0; i<state->imagewidth;i++) + fprintf(stderr, "Image Buffer is %f, count = %d\n", *(state->image+i), (i+1)); */ + + + + return ( SetPullableSource( state, sai_callback, "image.c stabilised image" ) ) ; +} + +/**************************************************************************** +* sai_callback +* The callback function at source->info->callback in the source +* returned by Sai. +* A "frame" is a window over the cochleagram, of size chans * points. +* A "segment" is a stretch of the cochleagram which is segsize (ie 50) time +* samples long, ie segsize columns of the cochleagram. +* +* This callback routine is executed once for each sai frame which is plotted. +* "*bytes" is the number of bytes in the sai, ie: +* *bytes = state->imagewidth * sizeof(DataType) +****************************************************************************/ +static Pointer sai_callback( state, bytes ) +struct _sai_state *state ; +ByteCount *bytes ; +{ + register long col, point ; + register long points = state->framestep; /* total num points under frame */ +#if defined( PC ) + int segment = ( MAX_BUFFER - state->imagewidth * sizeof ( DataType ) ) / state->chans / sizeof ( DataType ) * state->chans ; +#else + int segment = segsize * state->chans ; +#endif + register DataType *input ; /* cochleagram (input) data array */ + static int first=1; + static int test=0; + int chan; + int i; +/* #if defined( PC ) + test=1; +#else + if (state->SusThresh == 0) + segment= 30 * state->chans ; +#endif */ + +/* test++; */ + + + /* If size argument is zero, then by convention free space and return null */ + if( *bytes == 0 ) { + Pull( state->source, 0 ) ; + Delete( state->image ) ; + return ( DeletePullableSource( state ) ) ; + } + + /* Initially pull enough data to buffer transient info ahead of the trigger */ + /* Note, the pull operation is not valid when the size arg is zero. */ + /* The result would be a segmentation fault on the next pull, below. */ + +if (state->Stcrit==0 || state->Stcrit==1 || state->Stcrit==2 || state->Stcrit==3) + if (first) { + if (state->imagenwidth > 0) + input = PullItems(state->source, state->imagenwidth, DataType); + first = 0; + } + + + /* Search frame in segment blocks for trigger-points */ + for( point=0 ; point < points ; point += segment ) { + if( segment > points - point ) /* Finish at end of frame */ + segment = points - point ; + /* Get one segment of the input data */ + /* Pull 50 columns of data from the source, in buffer *input. */ + /* Keep one sai of data, from last pull, in buffer behind *input */ + input = PullItems(state->source, segment, DataType); + + for( col=0 ; col < segment ; col += state->chans ) { + /* Retard the "current" data point by transient buffer width */ + /* (This retards the trigger by the transient time, about 5ms) */ + if (state->Stcrit==0 || state->Stcrit==1 || state->Stcrit==2 || state->Stcrit==3) + state->trigger(input-state->imagenwidth, state) ; + else{ + state->trigger( input, state) ; /* -state->imagenwidth, state ) ; */ + /*fprintf(stderr, " %d ", points) ; */ } + input += state->chans ; /* next column */ + /* decay image periodically, once every `decay_time' columns */ + /* Lines moved down */ + if( --state->decay_count <= 0 ) { + decayImage( state ) ; + state->decay_count = state->decay_time ; + } + } /* for each col */ + } /* for each seg */ + + /* whatever the number of bytes requested, the image is always this size */ + *bytes = state->imagewidth * sizeof ( *state->image ) ; + +/* if (state->Stcrit==1 && state->SusThresh==0){ + if (test%5==0) + decayImage( state ); + test++;} + else if (state->Stcrit==1 && state->SusThresh>0){ changed 2 to 3 + if (test%10==0) + decayImage( state ); + test++;} + else{ */ + + return ( (Pointer) state->image ) ; + } + +/**************************************************************************** +* decayImage +* Periodically attenuate image to give computationally efficient image decay. +* Attenuate all points in the sai by a constant factor. +****************************************************************************/ +static void decayImage( state ) +struct _sai_state *state ; +{ + register DataType *image_ptr, *image_end ; +#ifdef FLOAT + register FLOAT image_decay = DECAY_SCALE ; +#endif + + image_end = state->image + state->imagewidth ; /* end of sai array */ +#ifdef FLOAT + for( image_ptr=state->image ; image_ptr < image_end ; ) + *image_ptr++ *= image_decay ; +#else + for( image_ptr=state->image ; image_ptr < image_end ; image_ptr++ ) + *image_ptr -= *image_ptr + DECAY_ROUND >> DECAY_SHIFT ; +#endif + return ; +} + +/**************************************************************************** +* doColumn +* Trigger algorithm for Sai. +* Installed as source->info->state->trigger. +* +****************************************************************************/ +static void doColumn( input, state ) +DataType *input ; +struct _sai_state *state ; +{ + + register int chan ; + float strobelag; + int s_lag; + + + short *STI; + static int check=0; + int storev; + char buf[2]; + short stipts=-2000; + short zeroval=0; + + STI=(short *)calloc(state->chans, sizeof(short)); + + strobelag = (double) (state->time)-(state->stlag); + + s_lag=(state->time)-(state->stlag); + + /* For each channel up column, add a row of data to sai if the */ + /* current point is non-zero. Add the row from the current point */ + /* plus state->imagenwidth, to display the transient (nwidth) info. */ + + +/* 000000000000000000000000 stcrit 0000000000000000000000000000000000 */ +/* Add on every point : Low Pass Filter */ + +if (state->Stcrit==0) + { + for( chan=0 ; chan < state->chans ; chan++ ) + { + addIn(input+(state->imagenwidth), chan, state); + output_simple_strobe_info(state, stipts); + } + } /* if (state->Stcrit==0) */ + + +/* 111111111111111111111111 stcrit 1111111111111111111111111111111111 */ +/* Add on every point greater than "stthresh_ai" (default 0) */ + +if (state->Stcrit==1) + { + for( chan=0 ; chan < state->chans ; chan++ ) + { + if (input[chan] > state->SusThresh ) + { + addIn(input+(state->imagenwidth), chan, state); + output_simple_strobe_info(state, stipts); + } + else if (input[chan] <=state->SusThresh) + output_simple_strobe_info(state, zeroval); + } + } /* if (state->Stcrit==1) */ + + +/* 222222222222222222222222 stcrit 2222222222222222222222222222222222 */ +/* Add on every Nap pulse peak */ + +if (state->Stcrit==2) + { + for( chan=0 ; chan < state->chans ; chan++ ) + { + if (state->isStrobe[chan]) /* Check to see if it is time to addIn */ + { + addIn(input+((state->imagenwidth)-2*(state->chans)), chan, state); + state->isStrobe[chan]=0; /* Reset strobe flag */ + doList_strobe_info(state, chan); + } + +/* Find a pulse peak routine */ + + if (!state->isPulse[chan]) /* Not in pulse, looking for pulse */ + { + state->thresh[chan]=input[chan]; + if (input[chan] > 0 /*state->thresh[chan] */ ) /* Start of a Nap pulse */ + state->isPulse[chan] =1; /* set in pulse flag */ + } + else /* In pulse, looking for peak */ + { + if (input[chan] < state->thresh[chan]) /* peak found */ + { + state->isPulse[chan]=0; + state->isStrobe[chan]=1; /* set strobe pending flag */ + } + else + state->thresh[chan]=input[chan]; + } + output_thresh_info(state, chan); + } + } /* if (state->stcrit==2) */ + + + +/* 333333333333333333333333 stcrit 3333333333333333333333333333333333 */ +/* Add on pulse peaks that exceed strobe threshold (temporal shadow ) */ +/* Primitive Local Max algorithm */ + +if (state->Stcrit==3) + { + for( chan=0 ; chan < state->chans ; chan++ ) + { + if (state->isStrobe[chan]) /* Check to see if it is time to addIn */ + { + addIn(input+((state->imagenwidth)-2*(state->chans)), chan, state); + state->isStrobe[chan] = 0; /* Reset strobe flag */ + doList_strobe_info(state, chan); + } + +/* Find a pulse peak routine */ + + if (!state->isPulse[chan]) /* Not in Pulse, looking for Pulse */ + { + if (input[chan] > state->thresh[chan] && input[chan] > state->previnput[chan]) + { /* Previnput check ensures threshold decay + does not cut into pulse */ + + state->isPulse[chan] = 1; /* set flag: in pulse */ + state->thresh[chan] = input[chan]; /* start search for peak */ + } + else { + decay_strobe_threshold(state, chan); + if (input[chan]==0) /* in the zeroes between nap pulses */ + state->previnput[chan] = 0; /* Set it to Zero previnput */ + } + } + + if (state->isPulse[chan]) /* Else in ongoing pulse looking for peak */ + { + if (input[chan] <= state->thresh[chan] && input[chan] <= state->previnput[chan]) + /* pulse peak found */ + { + state->isPulse[chan] = 0; /* clear flag: not in pulse */ + state->isStrobe[chan] = 1; /* set Strobe pending flag */ + if (!strcmp(exposwitch, "off")) + state->tlim[chan]=state->triggerdecay * state->thresh[chan]; + state->trigtime[chan]=(state->time)-1; + /* state->previnput[chan]=input[cha */ + } + else /* still in pulse, so continue search for peak */ + state->thresh[chan] = input[chan]; + + } /* if in Pulse */ + state->previnput[chan]=input[chan]; + output_thresh_info(state, chan); + } /* for all chans... */ + } /* if (state->Stcrit==3) */ + + + +/* 444444444444444444444444 stcrit 4444444444444444444444444444444444 */ +/* Add on pulse peaks that exceed strobe threshold and which are not succeeded by + a larger pulse in nwid ms. (Better Local Max mechanism) */ + +if (state->Stcrit==4) + { + for( chan=0 ; chan < state->chans ; chan++ ) + { + if (state->isStrobe[chan]) /* Check to see if it is time to addIn */ + { + if ( strobelag-1 >= state->trigtime[chan]) + /* initiates strobe after nwid ms */ + { + addIn(input-(state->chans), chan, state); + doList_strobe_info(state, chan); + state->isStrobe[chan] = 0; /* Reset strobe flag */ + state->trigheight[chan] = 0; /* Reset local max value */ + + } + } + +/* Find a pulse peak routine */ + + if (!state->isPulse[chan]) /* Not in Pulse, looking for pulse */ + { + if (input[chan] > state->thresh[chan] && input[chan] > state->previnput[chan]) + { /* Previnput check ensures threshold + decay does not cut into pulse */ + + state->isPulse[chan] = 1; /* set flag: in pulse */ + state->thresh[chan] = input[chan]; /* start search for peak */ + /* state->previnput[chan]= 1; in pulse till the next zero value */ + } + else + { + decay_strobe_threshold(state, chan); + if (input[chan]==0) /* in the zeroes between nap pulses */ + state->previnput[chan] = 0; /* Free previnput */ + } + } + + if (state->isPulse[chan]) /* Else in ongoing pulse, looking for peak */ + { + if (input[chan] <= state->thresh[chan] && input[chan] <= state->previnput[chan]) + /* pulse peak found */ + { + state->isPulse[chan] = 0; /* clear flag: not in pulse */ + state->isStrobe[chan] = 1; /* set Strobe pending flag */ + + if ( state->thresh[chan] > state->trigheight[chan] ) + /* Check for LOCAL MAX */ + { + state->trigheight[chan] = state->thresh[chan]; + state->trigtime[chan] = (state->time)-1; /* Set strobe time */ + if (!strcmp(exposwitch, "off")) + state->tlim[chan]=state->triggerdecay * state->trigheight[chan]; + } + } + else /* still in pulse, so continue search for peak */ + state->thresh[chan] = input[chan]; + } /* if in Pulse */ + state->previnput[chan]=input[chan]; + output_thresh_info(state, chan); + } /* for all chans... */ +} /* if (state->Stcrit==4) */ + + + +/* 555555555555555555555555 stcrit 5555555555555555555555555555555555 */ +/* Add on pulse peaks that exceed storbe threshold and which are not succeeded by + a larger pulse in nwid ms, provided total lag is < 2*nwid ms + Local Max mechanism with a timeout */ + +if (state->Stcrit==5) + { + for (chan = 0; chan < state->chans; chan++) + { + if(state->isStrobe[chan]) /* Check to see if it is time to addIn */ + { /* check for waiting till the first local max found */ + if (strobelag-1 >= state->def_strobe_candidate_time[chan]) + /* initiate strobe after nwid ms */ + { + /* print_trigger_debugging_info(state, chan, s_lag, 1); */ + addIn(input-state->chans, chan, state); + doList_strobe_info(state, chan); + + state->def_strobe_candidate_time[chan] = 0; /* Reset strobe time */ + state->def_strobe_height[chan] = 0; /* Reset local max value */ + state->trigheight[chan]=0; + state->isStrobe[chan]=0; /* Reset strobe flag */ + + } + } + +/* Find a pulse peak routine */ + + if (!state->isPulse[chan]) /* Not in pulse, looking for pulse */ + { + if (input[chan] > state->thresh[chan] && input[chan] > state->previnput[chan]) + { /* Previnput check ensures threshold + decay does not cut into pulse */ + + state->isPulse[chan] = 1; /* set flag: in pulse */ + state->thresh[chan] = input[chan]; /* start search for peak */ + /* state->previnput[chan] = 1; in pulse till the next zero value */ + } + else + { + decay_strobe_threshold(state, chan); + if (input[chan]==0) /* in the zeroes between nap pulses */ + state->previnput[chan]=0; /* Free previnput */ + } + + } + + if (state->isPulse[chan]) /* Else in ongoing pulse, looking for peak */ + { + if (input[chan] <= state->thresh[chan] && input[chan] <= state->previnput[chan]) + /* Pulse Peak Found */ + { + state->isPulse[chan]=0; /* clear flag: not in pulse */ + + if (locmax_searchstart[chan]==0 ) + /* && state->time>=((ttime[chan]-initial_strobe_candidate_time[chan])+ltime[chan])) AJD 1-2-96 */ + { /* start local max time (for timeout) + and shift search window */ + locmax_searchtime[chan]=1; /* local max time started */ + locmax_searchstart[chan]=1; /* local max flag set */ + print_trigger_debugging_info(state, chan, s_lag, 2); + initial_strobe_candidate_time[chan]=(state->time)-1; /* local max start time noted */ + + } + + /* if (locmax_searchstart[chan]==1) */ /* 8rd March, 1995 AJayDatta */ + if (state->thresh[chan] > state->trigheight[chan]) + /* Check for LOCAL MAX */ + { + state->trigheight[chan]=state->thresh[chan]; + state->trigtime[chan]=(state->time)-1; /* Set strobe time */ + if (!strcmp(exposwitch, "off")) + state->tlim[chan]=state->triggerdecay * state->trigheight[chan]; + if (locmax_searchstart[chan]==1) + print_trigger_debugging_info(state, chan, s_lag, 3); + } /* if greater than thresh */ + + + /* Reached end of time-out period (While in the peak finding stage !) + End of the timeout period can occur at two stages of the algorithm, + at a Nap pulse peak or at any point of the pulse train. */ + + if (locmax_searchtime[chan]==(state->stlag)) /* imagenwidth/state->chans)) */ + { + print_trigger_debugging_info(state, chan, s_lag, 4); + + state->def_strobe_candidate_time[chan]=state->trigtime[chan]; + state->def_strobe_height[chan]=state->trigheight[chan]; + locmax_searchtime[chan]=0; + locmax_searchstart[chan]=0; + state->trigheight[chan]=0; + state->trigtime[chan]=0; + state->isStrobe[chan]=1; /* set Strobe pending flag */ + ltime[chan]=state->time; /* End of search time */ + ttime[chan]=state->def_strobe_candidate_time[chan]; /* Local Max time */ + } + /* reset search period to zero */ + /* as y ms search period ends, update the info for strobe and + clear current_ values for the next y ms */ + } /* Pulse Peak Loop */ + else + state->thresh[chan]=input[chan]; + } /* if (state->isPulse..) */ + + if (locmax_searchtime[chan] == (state->stlag)) /* imagenwidth/state->chans)) */ + /* && state->def_strobe_candidate_time[chan]==0) */ + { /* start local max time (for timeout) */ + print_trigger_debugging_info(state, chan, s_lag, 5); + + state->def_strobe_candidate_time[chan]=state->trigtime[chan]; + state->def_strobe_height[chan]=state->trigheight[chan]; + locmax_searchtime[chan]=0; + locmax_searchstart[chan]=0; + state->trigheight[chan]=0; + state->trigtime[chan]=0; + state->isStrobe[chan]=1; /* set Strobe pending flag */ + ltime[chan]=state->time; + ttime[chan]=state->def_strobe_candidate_time[chan]; + } + /* reset search period to zero */ + /* as y ms search period ends, update the info for strobe and + clear current_ values for the next y ms */ + + + if (locmax_searchstart[chan]==1) + (locmax_searchtime[chan])++; + state->previnput[chan]=input[chan]; + output_thresh_info(state, chan); + + } /* for all chans */ +} /* if (state->Stcrit== 5) */ + + + qq=insertl(state->time); /* insert time to list */ + (state->time)++; + return; + +} /* doColumn */ + + +/**************************************************************************** +* addIn +* add row cochleagram into stabilised image +****************************************************************************/ +static void addIn( input, chan, state ) + DataType *input ; + int chan ; + struct _sai_state *state ; +{ + register DataType *channel_ptr, *image_ptr, *end; +#ifdef FLOAT + register FLOAT input_scale = INPUT_SCALE ; +#endif + register ScalarType *decayfactor = state->framedecay; + + /* Initialize channel_ptr to the input data to be added in. */ + /* `input' points to a particular column in the cochleagram, */ + /* and `chan' indexes a particular channel in this column. */ + channel_ptr = input + chan ; + end = channel_ptr - state->imagewidth ; + + /* Initialize image_ptr to end of sai row corresponding to `chan'.*/ + /* `state->image' points to the start of the sai, */ + /* so increment pointer by chan*framewidth to get to row `chan'. */ + /* To get to end of row, increment by ((chan+1)*framewidth)-1. */ + /* But framewidth = state->imagewidth / state->chans, and hence: */ + /* Added fix; as new spiral.c accepts nwid AJD 17-3-95 */ + if (state->Stcrit==4 && state->stlag==0) + image_ptr = (state->image + state->imagewidth / state->chans * (chan+1)); + /* 13-3-95 removed -1; ajd */ + else + image_ptr = (state->image + state->imagewidth / state->chans * (chan+1))-1; + + /* Decrement channel_ptr by columns, from the initial column, */ + /* until one complete sai row has been added into, which is when */ + /* channel_ptr has decreased by a whole imagewidth. */ + while( channel_ptr > end ) { +#ifdef FLOAT + *image_ptr += ( ( *channel_ptr * input_scale ) * (*decayfactor++) ) ; +#else + *image_ptr += DESCALE( ( *channel_ptr >> INPUT_SHIFT ) * (*decayfactor++) ) ; +#endif + /* if (state->chans <= 40) */ + if (*image_ptr > 32767.0) + *image_ptr = 32767.0; /* 32767 */ + + image_ptr--; + + channel_ptr -= state->chans ; /* next (ie previous) input time point */ + } + + return ; +} + + +/*************************************************************************** + * Summary is not called in this module. + * (see SummaryEntry in model.c, which is the entry point for gensas). + * Summary computes a row summary spectrogram and is called during the + * program "gensas". + + * Routines in this module it uses are: + * summary_callback + ****************************************************************************/ + +struct _summary_state { + struct _fillable_source parent ; + Source source; + int framewidth; /* number of time-points or cols in an sai */ + int frameheight; /* number of channels or rows in an sai */ + double scale; + int *llim; /* integration limits (lower & upper) for sai summary data */ + int *ulim; +}; + + +Source Summary( source, frameheight, scale, cfs, samplerate, llimstr, ulimstr) +Source source ; +int frameheight ; +double scale ; +double *cfs, samplerate ; /* array of channel centre frequencies */ +char *llimstr, *ulimstr; /* lower and upper limit strings */ +{ + int i, framewidth, max_ulim=0, min_llim=99999999; + + DeclareNew( struct _summary_state *, state ) ; + + /* Allocate new arrays for integration limits */ + state->llim = NewArray(int, frameheight, "image.c for lower limit array" ); + state->ulim = NewArray(int, frameheight, "image.c for upper limit array" ); + + for (i=0 ; i<frameheight ; i++) { + + /* Convert strings to sample points, allowing for cycles units */ + state->llim[i] = Cycles( llimstr, cfs[i], Samplerate() ); + state->ulim[i] = Cycles( ulimstr, cfs[i], Samplerate() ); + + /* Check that llim < ulim, and quit if not so */ + if (state->llim[i] >= state->ulim[i]) + stitch_error("Warning: gensas integration limits badly ordered\n"); + + /* Find limits on required framewidth */ + if (state->ulim[i] > max_ulim) + max_ulim = state->ulim[i]; + if (state->llim[i] < min_llim) + min_llim = state->llim[i]; + } + + if (min_llim > 0) min_llim = 0; + framewidth = max_ulim - min_llim; + + /* Adjust the integration limits for an sai with triggering on the */ + /* right. (The parameters llim <= ulim, but the resulting sai indices */ + /* state->llim[i] > state->ulim[i]). */ + /* This is done by subtracting from (framewidth-1), where framewidth is */ + /* the maximum required number of points, (ie ulim in points). */ + + for (i=0 ; i<frameheight ; i++) { + state->llim[i] = framewidth - state->llim[i] ; + state->ulim[i] = framewidth - state->ulim[i] ; + } + + framewidth++; /* extra point as array starts from zeroth location */ + + /* a blockings requests up into requests of the size specifed */ + state->source = NewBlockingSource( source, sizeof ( DataType ) * framewidth * frameheight ) ; + state->framewidth = framewidth ; + state->frameheight = frameheight ; + state->scale = scale ; + + return ( SetFillableSource( state, summary_callback, "image.c summarising sai" ) ) ; +} + +/*********************** Routines supporting Summary ***********************/ + +static Pointer summary_callback( state, bytes, buffer ) +struct _summary_state *state ; +ByteCount *bytes ; +DataType *buffer ; +{ + register int last = *bytes == 0 ; + register int i, j, ulim, llim, point, points=ToPoints(DataType,*bytes) ; + register DataType *sairow; +#ifdef FLOAT + register DataType sum ; +#else + register long sum ; +#endif + + /* Pull an sai frame */ + + for( point=0 ; point < points ; ) + /* For each channel (row) in the sai, sum the row between the given limits */ + for(i=0 ; i<state->frameheight ; i++) { + + sairow = PullItems(state->source,state->framewidth,DataType); + + ulim = state->ulim[i]; + llim = state->llim[i]; + sum = 0; + for (j=ulim ; j<=llim ; j++) + sum += sairow[j]; + + /* store the row-sum, scaled and normalized for the range of the sum */ + buffer[point++] = sum*state->scale / (llim-ulim); + } + + if( !last ) + return ( (Pointer) buffer ) ; + else { + Delete( state->llim ) ; + Delete( state->ulim ) ; + + return ( DeleteFillableSource( state ) ) ; + } +} + +/********************************************************************************/ +/* */ +/* initlist initialises the linked list pointer before the other list functions */ +/* call the list. */ +/* */ +/********************************************************************************/ +void initlist() +{ + static int status=0; + if (status==0) + start = endl = pp = qq = (node *)malloc(sizeof(node)); + status=1; +} + +/********************************************************************************/ +/* */ +/* Function getnode allocates storage for a link list node and returns a */ +/* pointer to that node */ +/* */ +/********************************************************************************/ +node *getnode() +{ + node *q; + q=(node *)malloc(sizeof(node)); + if (q==NULL) exit(66); + return q; +} + +/********************************************************************************/ +/* */ +/* The function insertl inserts the value x at the end of the linked list and */ +/* moves the end point by another node */ +/* */ +/********************************************************************************/ +node *insertl(x) +int x; +{ + node *q=endl; + endl=getnode(); + q->next=endl; + q->index=x; + q->val=0; + /* fprintf(testfile, "insertl: index is %d, val is %d\n", q->index, q->val); */ + return q; +} + +/********************************************************************************/ +/* */ +/* The function inss searches the list from the start, stops 3 nodes before the */ +/* value x, assigns the value -2000 to the three nodes till the (x+1)th node. */ +/* */ +/********************************************************************************/ +void inss(x) +int x; +{ + node *q=start; + int y=x-3; + int z=x; + while (q!=endl) + { + if (q->index == y) + do + { + q->val=-2000; + q=q->next; + /* fprintf(stderr, "Inss(x) index=%d\n",q->index); */ + } while (q->index < z); + q=q->next; + } +} + + +/********************************************************************************/ +/* */ +/* output_simple_strobe_info routine creates a file pointed by trigger file */ +/* which writes the points in the Nap pulse which initiates strobing */ +/* */ +/********************************************************************************/ +void output_simple_strobe_info(state, value) +struct _sai_state *state; +short value; +{ + +if (!(!strcmp(state->switch_info, "off") || !strcmp(state->switch_info, "on"))) + fwrite(&(value), sizeof(short), 1 , trigger_file); +} + +/********************************************************************************/ +/* */ +/* doList_strobe_info creates a file (pointed by "trigger_file") which writes */ +/* in binary shorts the points in the Nap pulse which initiates a strobe */ +/* It uses a linked list data structure to keep track of the actual time which */ +/* causes a strobe, rather than the time when the strobing event occurs. */ +/* At stcrit 4 or 5, strobing occurs nwid ms after a nap pulse peak has been */ +/* located. */ +/* */ +/********************************************************************************/ +void doList_strobe_info(state, chan) +struct _sai_state *state; +int chan; +{ + int storev; + + if (state->Stcrit==2) + storev=(state->time)-2; + else if (state->Stcrit==5) + storev=state->def_strobe_candidate_time[chan]; + /* fprintf(stderr, "Storev value of doList is = %d\n", storev);} */ + else + storev=state->trigtime[chan]; + + inss(storev); + + while (pp->index<=storev) /* (pp!=qq) */ + { + if (!(!strcmp(state->switch_info, "off") || !strcmp(state->switch_info, "on"))) + fwrite(&(pp->val), sizeof(short), 1, trigger_file); + /* fprintf(testfile, "doList: index is %d, val is %d\n", pp->index, pp->val); */ + pp=pp->next; + } + /* pp=qq; */ +} + +/********************************************************************************/ +/* */ +/* output_thresh_info writes threshold values (in binary shorts or floats) to */ +/* the file opened by the file pointer "thresho_file" */ +/* */ +/********************************************************************************/ +void output_thresh_info(state, chan) +struct _sai_state *state; +int chan; +{ + S[chan]=state->thresh[chan]; + +if (!(!strcmp(state->switch_info, "off") || !strcmp(state->switch_info, "on"))) +#ifdef FLOAT + fwrite((S+chan), sizeof(float), 1, thresho_file); +#else + fwrite((S+chan), sizeof(short), 1, thresho_file); +#endif + +} + +/********************************************************************************/ +/* */ +/* decay_strobe_threshold decays the strobe threshold at every clock */ +/* tick either linearly or exponentially */ +/* */ +/********************************************************************************/ +void decay_strobe_threshold(state, chan) +struct _sai_state *state; +int chan; +{ + + if (!strcmp(exposwitch, "on")) + state->thresh[chan] *= state->triggerdecay; + else + { + state->thresh[chan] -= state->tlim[chan]; + if (state->thresh[chan] < 0) + state->thresh[chan]=0; + } +} + +/********************************************************************************/ +/* */ +/* This routine prints debugging information of the local max search */ +/* with timeout (i.e. stcrit_ai=5) */ +/* */ +/********************************************************************************/ +void print_trigger_debugging_info(state, chan, strobe_lag, stage) +struct _sai_state *state; +int chan; +int strobe_lag; +int stage; +{ + if (stage==1) + { + if (strobe_lag > state->def_strobe_candidate_time[chan]) + val[chan]=strobe_lag-state->def_strobe_candidate_time[chan]; + if (strobe_lag == state->def_strobe_candidate_time[chan]) + val[chan]=0; + + if (!strcmp(state->switch_info,"off")); + else + fprintf(infotxt_file, "Chan %d: :: Actual time of trigger: s_lag=%d, trigtime=%d\n", chan, strobe_lag, state->def_strobe_candidate_time[chan]); + } + +/* fprintf(infotxt_file, "Chan %d: Actual time of trigger: s_lag=%d, trigtime=%d, shift=%d\n", chan, strobe_lag, state->def_strobe_candidate_time[chan], val[chan]*state->chans); */ + + + if (stage==2) + { + if (!strcmp(state->switch_info,"off")); + else + fprintf(infotxt_file, "Chan %d: Loc Max Start Time = %d, trigcurrent %d, Threshold=%f, shift=%d\n", chan, state->time, state->trigheight[chan], state->thresh[chan], (ttime[chan]-initial_strobe_candidate_time[chan])); + } + + if (stage==3) + { + if (!strcmp(state->switch_info,"off")); + else + fprintf(infotxt_file, "Chan %d: Loc Max is %d Time is %d\n", chan, state->trigheight[chan], state->trigtime[chan] ); + } + + if (stage==4) + { + if (!strcmp(state->switch_info,"off")); + else + fprintf(infotxt_file, "Chan %d: At the End of Search (At peak), trigtime = %d time = %d height = %d\n", chan, state->trigtime[chan], state->time, state->trigheight[chan]); + } + if (stage==5) + { + if (!strcmp(state->switch_info,"off")); + else + fprintf(infotxt_file, "Chan %d: At the End of Search, trigtime = %d time = %d height = %d\n", chan, state->trigtime[chan], state->time, state->trigheight[chan]); + } + + +} +