Mercurial > hg > aim92
diff wdf/wdf_tl.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/wdf/wdf_tl.c Fri May 20 15:19:45 2011 +0100 @@ -0,0 +1,637 @@ +/* + 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. + + THE MRC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING + ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE + A.P.U. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY + DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/* + Acknowledgment: + ============== + + The source code provided in this file was originally developed by + Christian Giguere as part of a Ph.D degree at the Department of + Engineering of the University of Cambridge from April 1990 to + November 1993. The code was subsequently adapted under a grant + from the Hearing Research Trust for full compatibility with + AIM Release 6.15. + + Christian Giguere 25/03/94 + +*/ + +/* + ============================================================= + wdf_tl.c + ============================================================= + + Wave digital filter (WDF) implementation of the cochlear + transmission line (TL) network. + + Author : Christian Giguere + First written : 01st April, 1991 + Last edited : 18th February, 1994 + + References: + (1) C.Giguere and P.C.Woodland (1994). JASA 95(1): 331-342. + (2) A.Fettweis (1986). Proceedings IEEE 74(2): 270-327. + ============================================================= +*/ + +/***** includes *****/ + +#include <math.h> +#include <stdio.h> +#include "stitch.h" +#include "source.h" +#include "calc.h" +#include "calc_tl.h" +#include "bank_tl.h" +#include "wdf_tl.h" +#include "wdf_ear.h" + +/***** defines *****/ + +#if 0 +#define _DEBUG_ +#define _OVERFLOW_ +#endif + +#if 0 +#define _IMPULSE_ /* bypasses input wave with a delta impulse */ +#endif + +#if 0 /* bypasses input wave with a pure tone and dumps */ +#define _BMCURVES_ /* the rms value of basilar membrane velocity on stdout */ +#endif /* (see below for other parameters to set) */ + +#if 0 +#define _EAR_ /* dumps stapes velocity and eardrum pressure on stdout */ +#endif + +#define NUMBER_OF_STATES ( 14 ) /* per TLF segment */ +#define NUMBER_OF_COEFFS ( 0 ) /* per TLF segment */ + + +/***** functions *****/ + +/******************************** WDF set-up function ************************************* +* name: function: +* +* WDFilterState() Set-up function for the WDF implementation of the cochlear network +* (Giguere and Woodland, 1994, Figs. 3 and 10). It generates the +* multiplier coefficients and initializes the states of the filter. +* It is called by function ``TLF_GenBank()'' in file ``bank_tl.c'' +* once for each segment of the transmission line starting from the most +* apical segment. It returns a pointer to a structure containing all +* the relevant information for the computation of the current filter +* segment. +* +* Note: The code that sets up the WDF implementation of the outer ear +* and middle ear is located in file ``wdf_ear.c''. +******************************************************************************************/ + +/************************************ WDFilterState() ************************************/ + +WDFilterState *WDFilter( samplerate, center_frequency, scale_vel, scale_disp, rho, scala_area, + width, qfactor, mass_per_area, seglength, Lt, warping, active, zov, + OHC_gain, OHC_sat ) + +double samplerate, center_frequency, scale_vel, scale_disp ; +double rho, scala_area, width, qfactor, mass_per_area, seglength, Lt ; +int warping, active ; +double *zov, OHC_gain, OHC_sat ; +{ + static int apex = 1 ; + DeclareNew( WDFilterState *, filter_state ) ; + CoeffType *cf ; + double an, bn, mn, d, fs, fn ; + double Lsn, Ln, Cn, Rn ; + double r1, r2, r3, r4, g1, g2, g3, zn, zcn ; + + /***** cochlear parameters for current BM segment *****/ + an = scala_area ; /* cross-sectional area of each scala (cm2) */ + bn = width ; /* BM width (cm) */ + mn = mass_per_area ; /* transversal mass-per-area-of-BM (g/cm2) */ + d = seglength ; /* BM segment length (cm) */ + + /***** frequency warping compensation due to bilinear transform *****/ + fs = samplerate ; + if ( warping == 0 ) + fn = center_frequency ; + else + fn = fs / Pi * tan( Pi * center_frequency / fs ) ; + + /***** electrical circuit elements (CGS units) for current segment *****/ + Ln = mn / ( bn * d ) ; /* shunt inductor */ + Cn = 1. / ( ( TwoPi * TwoPi * fn * fn ) * Ln ) ; /* shunt capacitor */ + Rn = sqrt( Ln / Cn ) / qfactor ; /* shunt resistor */ + Lsn = ( 2. * rho * d ) / an ; /* series inductor */ + + + /***** WDF multiplier coefficients for current TL segment *****/ + + /*** initialise filter coeffs ***/ + filter_state->coeffs = ( char * ) NewZeroedArray( CoeffType, NUMBER_OF_COEFFS, + "wdf_tl.c for filter coeffs\n" ) ; + cf = ( CoeffType * ) filter_state->coeffs ; + + /* Adaptor 25 */ + r1 = zcn = 1. / (2. * fs * Cn ) ; /* Zcn */ + r2 = Rn ; /* Zrn */ + r3 = 2. * fs * Ln ; /* Zln */ + r4 = zn = r1 + r2 + r3 ; + filter_state->gamma251 = r1 / r4 ; + filter_state->gamma252 = r2 / r4 ; + + /* Adaptor 24 */ + g1 = 1. / zn ; + if( apex ) { + g2 = 1. / ( 2. * fs * Lt ) ; /* (1 / Zlt) */ + apex = 0 ; + } + else + g2 = 1. / *zov ; /* Zov (at the base) */ + g3 = g1 + g2 ; + filter_state->gamma241 = g1 / g3 ; + + /* Adaptor 23 */ + r1 = 2. * fs * Lsn ; /* Zlsn */ + r2 = 1. / g3 ; + r3 = *zov = r1 + r2 ; /* Zov */ + filter_state->gamma231 = r1 / r3 ; + +#ifdef _DEBUG_ + fprintf( stderr, "gamma251=%f gamma252=%f\n", filter_state->gamma251 + , filter_state->gamma252 ) ; + fprintf( stderr, "gamma241=%f \n", filter_state->gamma241 ) ; + fprintf( stderr, "gamma231=%f \n", filter_state->gamma231 ) ; +#endif + + /***** scaling to convert output to transversal motion of BM segment *****/ + filter_state->out_scale_disp = scale_disp * Cn / ( 2. * bn * d ) ; + filter_state->out_scale_vel = scale_vel / ( 2. * zcn * bn * d ) ; + + /***** multiplier coefficients for OHC source *****/ + filter_state->OHC_sat = OHC_sat * scale_disp / filter_state->out_scale_disp ; + filter_state->OHC_gain = - OHC_gain * Rn * filter_state->OHC_sat / ( 2. * zcn ) ; + +#ifdef _BMCURVES_ + /***** initialise rms detection *****/ + filter_state->rms = 0.0 ; + filter_state->sample = 0 ; +#endif + + /***** initialise filter states *****/ + filter_state->states = ( char * ) NewZeroedArray( StateType, NUMBER_OF_STATES, + "wdf_tl.c for filter states\n" ) ; + + /***** is channel active for display ? *****/ + filter_state->active = active ; + + /***** return *****/ + return( filter_state ) ; +} + + +/*********************************** WDF procedures *************************************** +* name: function: +* +* DoWDFdataArray() WDF realization of the outer ear, middle ear and cochlear networks. +* It is called by function ``tlf_bank_callback()'' in file ``bank_tl.c'' +* once for a specified number of input points. It computes the BM +* displacement or velocity for each segment and keeps track of the +* filter states. +* +* CloseWDF() Deletes all private data structures and arrays of the cochlear +* transmission line filter upon completion of filtering. It is called +* by function ``tlf_bank_callbank()'' in file ``bank_tl.c''. +******************************************************************************************/ + +/********************************** DoWDFdataArray() **********************************/ + +void DoWDFdataArray( bankInfo, filter_states, input, output, points, channels, + ws, eartube_states, ms, tube_segments ) +register TLF_BankInfo *bankInfo ; +register WDFilterState **filter_states ; +register DataType *input ; +register DataType *output ; +register int points, channels ; +register WaveWDFstate *ws ; +register EartubeWDFstate **eartube_states ; +register EarmiddleWDFstate *ms ; +register int tube_segments ; +{ + register WDFilterState *fs ; + register EartubeWDFstate *ts ; + register StateType *st ; + register StateType dn, in, out ; + register StateType a0, an_1, b1, b2, b3, b63, b161, b202, b212 ; + static StateType bn = 0. ; + register CoeffType *cf ; + register int decimateCount = bankInfo->decimateCount ; + register int decimate_factor = bankInfo->decimate_factor ; + register int channelActive, pointActive, motion = bankInfo->output_var ; + register int channel = -1, tube_segment = -1 ; +#ifdef _BMCURVES_ + static long sample = -1 ; + int period = 48 ; /* in samples */ + double samplerate = 48000. ; /* samples per sec */ + double rise_time = 2.0 ; /* sec */ + double start_time = 4.0 ; /* sec */ +#endif +#ifdef _IMPULSE_ + static int impulse = 10000 ; +#endif + + /***** update stapedial muscle state *****/ + + ( void ) ms->update_proc( ms ) ; + + /***** for all points ******/ + +#ifdef _DEBUG_ + fprintf( stderr, "DoWDFdataArray() = %d points X %d channels\n", points, channels ) ; +#endif + + while( points-- > 0 ) { + + if( decimateCount == decimate_factor ) + decimateCount = 0 ; + pointActive = !decimateCount ; + decimateCount++ ; + + output += bankInfo->output_chans * pointActive ; + + /*** for all TL channels in BACKWARD direction for current input point ***/ + + while( ++channel < channels ) { + + /* get states and coefficients */ + fs = *filter_states++ ; + st = ( StateType * ) fs->states ; + cf = ( CoeffType * ) fs->coeffs ; + + /* Adaptor 25 */ + st[10] = st[1] ; /* a251 */ + st[3] = st[2] - st[4] - st[1] ; /* b254 = a221 */ + + /* Adaptor 24 */ + st[9] = bn ; /* -a242 */ + st[5] = fs->gamma241 * ( st[3] + st[9] ) ; /* b240 */ + st[6] = st[5] - st[9] ; /* b243 = a232 */ + + /* Adaptor 23 */ + st[7] = st[8] - st[6] ; /* b233 */ + bn = st[7] ; + } + + /*** input point ***/ + + in = ( StateType ) *input++ ; + +#ifdef _IMPULSE_ + if( impulse != 0 ) { + in = ( StateType ) impulse ; + impulse = 0 ; + } + else + in = ( StateType ) 0 ; +#endif + +#ifdef _BMCURVES_ + if( ++sample < ( long ) ( samplerate * rise_time ) ) + in = 0.28284 * ( double ) sample / ( samplerate * rise_time ) * + sin( TwoPi * ( double ) ( sample % period ) / ( double ) period ) ; + else + in = 0.28284 * sin( TwoPi * ( double ) ( sample % period ) / ( double ) period ) ; +#endif + + /*** computation of outer and middle ear in FORWARD direction for current input point ***/ + + /* freefield - external ear */ + + /* get states */ + st = ( StateType * ) ws->states ; + + /* Adaptor 0 */ + st[4] = ws->gamma01 * ( st[3] - in ) ; /* b00 */ + st[5] = st[4] + in + in ; /* b03 = -a11 */ + + /* Adaptor 2 */ + st[1] = -ws->gamma21 * st[0] ; /* b20 */ + st[2] = st[1] ; /* b23 = a12 */ + + /* Adaptor 1 */ + st[6] = st[5] - st[2] ; /* b13 = a32 */ + an_1 = st[6] ; + + /* concha and auditory canal */ + + while( ++tube_segment < tube_segments ) { + + /* get states */ + ts = *eartube_states++ ; + st = ( StateType * ) ts->states ; + + /* Adaptor 3 */ + st[3] = st[4] - an_1 ; /* b33 = a41 */ + + /* Adaptor 5 */ + st[0] = -ts->gamma51 * st[1] ; /* b50 */ + st[2] = st[0] + st[1] ; /* b53 = a42 */ + + /* Adaptor 4 */ + st[5] = st[3] - st[2] ; /* -a42+a41 */ + st[6] = ts->gamma41 * st[5] ; /* b40 */ + st[7] = st[6] + st[2] ; /* b43 = a61 */ + + /* Adaptor 6 */ + st[8] = st[9] - st[7] ; /* b63 = a32 */ + an_1 = b63 = st[8] ; + } + + /* middle ear */ + + /* get states */ + st = ( StateType * ) ms->states ; + + /* Adaptor 9 */ + st[31] = st[30] - st[29] ; /* b94 = a81 */ + + /* Adaptor 8 */ + st[32] = st[35] - st[31] ; /* a83-a81 */ + st[33] = -ms->gamma81 * st[32] - ms->gamma82 * st[35] ; /* b80 */ + st[34] = st[33] + st[35] ; /* b84 = a71 */ + + /* Adaptor 7 */ + st[36] = -st[34] - an_1 ; /* b73 = a102 */ + + /* Adaptor 13 */ + st[15] = -st[14] ; /* b133 = a122 */ + + /* Adaptor 12 */ + st[16] = st[15] + st[18] ; /* a122-a121 */ + st[17] = -ms->gamma121 * st[16] ; /* b120 */ + st[19] = st[17] + st[15] ; /* b123 = a112 */ + + /* Adaptor 14 */ + st[21] = -ms->gamma141 * st[20] ; /* b140 */ + st[22] = st[21] + st[20] ; /* b143 = a111 */ + + /* Adaptor 15 */ + st[23] = -st[24] ; /* b153 = a113 */ + + /* Adaptor 11 */ + st[25] = -st[22] - st[19] - st[23] ; /* b114 = a101 */ + + /* Adaptor 10 */ + st[26] = st[25] - st[36] ; /* -a102+a101 */ + st[27] = ms->gamma101 * st[26] ; /* b100 */ + st[28] = st[27] + st[36] ; /* b103 = a161 */ + + /* Adaptor 17 */ + st[10] = st[12] - st[11] ; /* b174 = a162 */ + + /* Adaptor 16 */ + st[13] = -st[28] - st[10] ; /* b163 = a181 */ + + /* Adaptor 19 */ + st[5] = -st[6] ; /* b193 = a182 */ + + /* Adaptor 18 */ + st[7] = st[13] - st[5] ; /* -a182+a181 */ + st[8] = ms->gamma181 * st[7] ; /* b180 */ + st[9] = st[8] + st[5] ; /* b183 = a203 */ + + /* Adaptor 20 */ + st[40] = - bn / ms->ratio ; /* a202 */ + st[3] = -st[40] - st[9] ; /* b204 = a212 */ + + /* Adaptor 21 */ + st[0] = st[2] - st[1] - st[3] ; /* b214 = a222 */ + + + /*** computation of outer and middle ear in BACKWARD direction for current input point ***/ + + /* middle ear */ + + /* Adaptor 22 */ + a0 = st[4] + st[0] ; /* a220 */ + st[4] = st[4] - ms->gamma221 * a0 ; /* b221 */ + b2 = -st[4] - a0 ; /* b222 = a214 */ + + /* Adaptor 21 */ + a0 = b2 - st[0] ; /* a210 */ + st[1] = st[1] - ms->gamma211 * a0 ; /* b211 */ + b212 = st[3] - ms->gamma212 * a0 ; /* b212 = a204 */ + st[2] = -st[1] - b212 - b2 ; /* b213 */ + + /* Adaptor 20 */ + a0 = b212 - st[3] ; /* a200 */ + b202 = st[40] - ms->gamma202 * a0 ; /* b202 */ + b3 = ms->gamma201 * a0 - b202 - b212 ; /* b203 = a183 */ + + /* Adaptor 18 */ + b2 = st[8] + b3 ; /* b182 = a193 */ + b1 = b2 - st[7] ; /* b181 = a163 */ + + /* Adaptor 19 */ + st[6] = st[6] + ms->gamma191 * ( st[5] - b2 ) ; /* b191 */ + + /* Adaptor 16 */ + b161 = st[28] + ms->gamma161 * ( st[13] - b1 ) ; /* b161 = a103 */ + b2 = -b161 - b1 ; /* b162 = a174 */ + + /* Adaptor 17 */ + a0 = b2 - st[10] ; /* a170 */ + st[11] = st[11] - ms->gamma171 * a0 ; /* b171 */ + st[12] = ms->gamma172 * a0 - b2 - st[11] ; /* b173 */ + + /* Adaptor 10 */ + st[37] = st[27] + b161 ; /* b102 = a73 */ + st[38] = st[37] - st[26] ; /* b101 = a114 */ + + /* Adaptor 11 */ + a0 = st[38] - st[25] ; /* a110 */ + b1 = st[22] - ms->gamma111 * a0 ; /* b111 = a143 */ + b2 = st[19] - ms->gamma112 * a0 ; /* b112 = a123 */ + b3 = -b1 -b2 - st[38] ; /* b113 = a153 */ + + /* Adaptor 15 */ + st[24] = st[24] + ms->gamma151 * ( st[23] - b3 ) ; /* b151 */ + + /* Adaptor 14 */ + st[20] = st[21] + b1 ; /* b142 */ + + /* Adaptor 12 */ + b2 = st[17] + b2 ; /* b122 = a133 */ + st[18] = b2 + st[16] ; /* b121 */ + + /* Adaptor 13 */ + st[14] = st[14] + ms->gamma131 * ( st[15] - b2 ) ; /* b131 */ + + /* Adaptor 7 */ + b1 = st[34] + ms->gamma71 * ( st[36] - st[37] ) ; /* b71 = a84 */ + st[39] = -b1 - st[37] ; /* b72 = a63 */ + + /* Adaptor 8 */ + st[35] = st[33] + b1 ; /* b83 */ + b1 = st[35] + st[32] ; /* b81 = a94 */ + + /* Adaptor 9 */ + a0 = b1 - st[31] ; /* a90 */ + st[29] = st[29] - ms->gamma91 * a0 ; /* b91 */ + st[30] = -st[29] + ms->gamma92 * a0 - b1 ; /* b93 */ + + bn = st[39] ; + + /* concha and auditory canal */ + + while( tube_segment-- > 0 ) { + + /* get states */ + ts = *--eartube_states ; + st = ( StateType * ) ts->states ; + + /* Adaptor 6 */ + b1 = st[7] - ts->gamma61 * ( bn - st[8] ) ; /* b61 = a43 */ + st[9] = -b1 - bn ; /* b62 */ + + /* Adaptor 4 */ + b2 = st[6] + b1 ; /* b42 = a53 */ + b1 = b2 - st[5] ; /* b41 = a33 */ + + /* Adaptor 5 */ + st[1] = st[0] + b2 ; /* b52 */ + + /* Adaptor 3 */ + st[4] = ts->gamma31 * ( st[3] - b1 ) - st[4] ; /* b31 */ + bn = -st[4] - b1 ; /* b32 = a63 */ + } + + /* freefield - external ear */ + + /* get states */ + st = ( StateType * ) ws->states ; + + /* Adaptor 1 */ + b1 = ws->gamma11 * ( st[6] - bn ) - st[5] ; /* b11 = -a03 */ + b2 = -b1 - bn ; /* b12 = a23 */ + + /* Adaptor 2 */ + st[0] = st[1] + b2 + st[0] ; /* b61 */ + + /* Adaptor 0 */ + st[3] = b1 - st[4] + st[3] ; /* -b01 + p */ + + an_1 = - b202 * ms->ratio ; + + + /* for all channels in FORWARD direction for current input point */ + + while( channel-- > 0 ) { + + /* get states and coefficients */ + fs = *--filter_states ; + st = ( StateType * ) fs->states ; + cf = ( CoeffType * ) fs->coeffs ; + channelActive = fs->active ; + + /* Adaptor 23 */ + st[8] = fs->gamma231 * ( st[7] - an_1 ) - st[8] ; /* b231 */ + b2 = an_1 + st[8] ; /* -b232 = -a243 */ + + /* Adaptor 24 */ + an_1 = b2 - st[5] ; /* -b242 */ + b1 = -an_1 - st[3] - st[9] ; /* b241 = a254 */ + + /* Adaptor 25 */ + a0 = b1 - st[3] ; /* a250 */ + st[1] = st[10] - fs->gamma251 * a0 ; /* b251 */ + st[2] = fs->gamma252 * a0 - st[4] - st[1] - b1 ; /* b253 */ + dn = st[1] + st[10] ; + in = st[1] - st[10] ; + + /* output storage */ + if( channelActive * pointActive ) { + + if( motion == DISPLACEMENT ) + out = fs->out_scale_disp * dn ; + else + out = fs->out_scale_vel * in ; +#ifdef _OVERFLOW_ + if( out > _MaxOutput_ || out < _MinOutput_ ) + fprintf( stderr, "Overflow error in BMM output=%e\n", ( double ) out ) ; +#endif + *(--output) = ( DataType ) ( out ) ; + } + + /* OHC nonlinear voltage source */ + if( dn < 0. ) + dn = -dn ; + st[4] = fs->OHC_gain * in / ( fs->OHC_sat + dn ) ; /* -Vn(ohc) */ + +#ifdef _BMCURVES_ + if( sample > ( long ) ( start_time * samplerate ) ) { + in = fs->out_scale_vel * in ; + fs->rms = fs->rms + in*in ; + fs->sample++ ; + } +#endif + + } + bn = -an_1 ; /* apical boundary condition */ + output += bankInfo->output_chans * pointActive ; + +#ifdef _EAR_ + st = ( StateType * ) ms->states ; + printf( "stapes=%e eardrum=%e\n", 0.5 * ( st[40] - b202 ), 0.5 * ( st[39] + b63 ) ) ; +#endif + + } + + return ; +} + + +/******************************* CloseWDF() *************************************/ + +void CloseWDF( states, channels, bank ) + register WDFilterState **states ; + register int channels ; + register TLF_BankInfo *bank ; +{ + int channel ; + + for( channel = 0 ; channel < channels ; channel++ ) { +#ifdef _BMCURVES_ + printf( "%d %e\n", channel+1, sqrt(states[channel]->rms / states[channel]->sample) ) ; +#endif + Delete( states[channel]->states ) ; + Delete( states[channel]->coeffs ) ; + } + Delete( states ) ; + + Delete( bank ) ; + + return ; + +} +