Mercurial > hg > aim92
view 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 source
/* 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]); } }