diff wdf/wdf_ear.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_ear.c	Fri May 20 15:19:45 2011 +0100
@@ -0,0 +1,844 @@
+/*
+    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_ear.c 
+    ===========================================================
+
+    Wave digital filter (WDF) implementation of the outer and 
+    middle ear (EAR) electroacoustic networks.
+
+    Author        : Christian Giguere
+    First written : 01st June, 1991
+    Last edited   : 18th February, 1994
+    
+    References:
+    (1) C.Giguere and P.C.Woodland (1992). Technical Report
+	CUED/F-INFENG/TR.93, University of Cambridge.
+    (2) C.Giguere and P.C.Woodland (1994). JASA 95(1): 331-342.
+    (3) M.E.Lutman and A.M.Martin (1979). J.Sound.Vib. 64(1): 133-157
+    (4) A.Fettweis (1986). Proceedings IEEE 74(2): 270-327.
+
+    Note: Ref (1) describes an implementation where the outer 
+	  ear and middle ear module is _UNCOUPLED_ to the cochlea. 
+	  In this case, the middle ear network is that of Lutman 
+	  and Martin (1979) unmodified.
+
+	  Ref (2) describes an implementation where the outer
+	  ear and middle ear module is _COUPLED_ to the cochlea.
+	  In this case, the middle ear network is a modified
+	  version of Lutman and Martin (1979) network (See Fig.2)
+    ===========================================================
+*/
+
+
+/***** includes *****/
+
+#include <math.h>
+#include <stdio.h>
+#include "stitch.h"
+#include "calc.h"
+#include "calc_tl.h"
+#include "wdf_ear.h"
+
+/***** defines *****/
+
+#define   N_OF_FREEFIELD_STATES    (           12 )
+#define   N_OF_EARTUBE_STATES      (           16 )      /* per segment */
+#define   N_OF_EARMIDDLE_STATES    (           48 )  
+
+#if 0
+#define   _DEBUG_   
+#define   _OVERFLOW_
+#endif
+
+#if 0
+#define   _IMPULSE_     	   /* bypasses input wave with a delta impulse */
+#endif
+
+#if 0
+#define   _EARDRUM_		   /* outputs eardrum pressure instead of stapes velocity */
+#endif
+
+/* Middle ear circuit elements in CGS units (From Lutman and Martin (1979) */
+
+#define   La                       (     14.0e-03 )      /* Middle ear cavities */
+#define   Cp                       (      5.1e-06 )     
+#define   Ra                       (         10.0 )    
+#define   Rm                       (        390.0 )    
+#define   Ct                       (     0.35e-06 )    
+#define   Rd1                      (        200.0 )      /* Eardrum losses */
+#define   Cd1                      (      0.8e-06 )    
+#define   Cd2                      (      0.4e-06 )    
+#define   Rd2                      (        220.0 )    
+#define   Ld                       (     15.0e-03 )    
+#define   Rd3                      (       5900.0 )    
+#define   Cd3                      (      0.2e-06 )    
+#define   Lo                       (     40.0e-03 )      /* Eardrum, mallus, incus */
+#define   Co                       (      1.4e-06 )    
+#define   Ro                       (         70.0 )    
+#define   Cs                       (     0.25e-06 )      /* Incudo-stapedial joint */
+#define   Rs                       (       3000.0 )    
+#define   Lc                       (     45.0e-03 )      /* Stapes and cochlea */
+#define   Cc                       (      0.6e-06 )    
+#define   Rc                       (        550.0 )    
+
+/* Additional middle ear circuit elements in CGS units (From Giguere and Woodland (1994) */
+
+#define   Ral                      (        100.0 )      /* annular ligaments */
+#define   Kst_max                  (   1./0.1e-06 )      /* max. Stapedius stiffness */
+#define   Kst_min_ratio            (       0.0001 )      /* min value of Kst_ratio */
+
+
+/***** external variables *****/
+
+double Kst_ratio = Kst_min_ratio ;                       /* initial fraction of Kst_max */
+							 /* updated by feedback module */
+static double rn_1 ; 
+
+
+/***** functions *****/
+
+/******************************* WDF set-up functions ************************************
+* name:                 function:
+*
+* FreefieldWDF()        Set-up function for the WDF implementation of the external ear network
+*                       (Giguere and Woodland, 1994, Figs. 1 and 8). It generates the multiplier 
+*                       coefficients and initializes the states of the filter. 
+*
+*                       In the case of the _UNCOUPLED_ ear version, it is called by the 
+*                       function ``Ear()'' in file ``ear.c''.
+*                       In the case of the _COUPLED_ ear version, it is called by the 
+*                       function ``TLF_GenBank()'' in file ``bank_tl.c''.
+*
+*                       It returns a pointer to a structure containing all the relevant 
+*                       information for the computation of the whole filter.
+*
+* EartubeWDF()          Set-up function for the WDF implementation of the concha and auditory
+*                       canal transmission lines (Giguere and Woodland, 1994, Figs. 1 and 8). 
+*                       It generates the multiplier coefficients and initializes the states 
+*                       of the filter.
+*
+*                       In the case of the _UNCOUPLED_ ear version, it is called by the 
+*                       function ``Ear()'' in file ``ear.c'' once for each segment of the 
+*                       transmission lines starting from the outermost segment.
+*                       In the case of the _COUPLED_ ear version, it is called by the 
+*                       function ``TLF_GenBank()'' in file ``bank_tl.c'' once for each segment 
+*                       of the transmission lines starting from the outermost segment.
+*
+*                       It returns a pointer to a structure containing all the relevant 
+*                       information for the current filter segment.
+*
+* EarmiddleWDF()        Set-up function for the WDF implementation of the middle ear network
+*                       (Giguere and Woodland, 1994, Figs. 2 and 9). It generates the multiplier 
+*                       coefficients and initializes the states of the filter. 
+*
+*                       In the case of the _UNCOUPLED_ ear version, it is called by the 
+*                       function ``Ear()'' in file ``ear.c''.
+*                       In the case of the _COUPLED_ ear version, it is called by the 
+*                       function ``TLF_GenBank()'' in file ``bank_tl.c''.
+*
+*                       It returns a pointer to a structure containing all the relevant 
+*                       information for the computation of the whole filter.
+******************************************************************************************/
+
+/************************** FreefieldWDF() *****************************/
+
+WaveWDFstate *FreefieldWDF( samplerate, rho, velocity, diffraction_radius, radiation_radius )
+    double  samplerate ;
+    double  rho, velocity ;
+    double  diffraction_radius, radiation_radius ;
+{
+    DeclareNew( WaveWDFstate *, filter_state ) ;
+    double  fs2 ;
+    double  r1, r2, r3, g1, g2, g3 ;
+    double  zh, zr ;
+    double  Lh, Rh, Lr, Rr ;
+ 
+   /*** electrical circuit elements (CGS) ***/
+    Rh = rho * velocity / ( Pi * diffraction_radius * diffraction_radius ) ;
+    Lh = 0.5 * rho / ( Pi * diffraction_radius ) ;
+    Rr = rho * velocity / ( Pi * radiation_radius * radiation_radius ) ;
+    Lr = 0.7 * rho / ( Pi * radiation_radius ) ;
+
+   /*** sampling frequency ***/
+    fs2 = samplerate * 2. ;
+ 
+   /*** WDF-port resistances and multiplier coefficients ***/
+ 
+    /* Adaptor 0 */
+    g1 = 1. / ( fs2 * Lh ) ;                                    /* (1 / Zlh) */
+    g2 = 1. / Rh ;                                              /* (1 / Zrh) */
+    g3 = g1 + g2 ;
+    zh = 1. / g3 ;
+    filter_state->gamma01 = g1 / g3 ;
+
+    /* Adaptor 2 */
+    g1 = 1. / ( fs2 * Lr ) ;                                    /* (1 / Zlr) */
+    g2 = 1. / Rr ;                                              /* (1 / Zrr) */
+    g3 = g1 + g2 ;
+    zr = 1. / g3 ;
+    filter_state->gamma21 = g1 / g3 ;
+
+    /* Adaptor 1 */
+    r1 = zh ;
+    r2 = zr ;
+    r3 = rn_1 = r1 + r2 ;
+    filter_state->gamma11 = r1 / r3 ;
+
+#ifdef _DEBUG_
+    fprintf( stderr, "Rh=%e  Lh=%e  Rr=%e  Lr=%e\n", Rh, Lh, Rr, Lr ) ;
+    fprintf( stderr, "gamma01=%f\n", filter_state->gamma01 ) ;
+    fprintf( stderr, "gamma11=%f\n", filter_state->gamma11 ) ;
+    fprintf( stderr, "gamma21=%f\n", filter_state->gamma21 ) ;                                            
+#endif
+
+   /*** initialise filter states ***/
+    filter_state->Nstates = N_OF_FREEFIELD_STATES ;
+    filter_state->states = ( char * ) NewZeroedArray( StateType, filter_state->Nstates,
+						      "wdf_ear.c for freefield states\n" ) ;
+
+   /*** return ***/
+    return( filter_state ) ;
+}
+
+/************************** EartubeWDF() *****************************/
+
+EartubeWDFstate *EartubeWDF( samplerate, rho, velocity, diam, seglength, attn )
+    double  samplerate ;
+    double  rho, velocity ;
+    double  diam, seglength ;
+    double  attn ;
+{
+    DeclareNew( EartubeWDFstate *, filter_state ) ;
+    double  an, d, c, k, alpha, fs2 ;
+    double  Ln, Cn, Gn, Zn ;
+    double  r1, r2, r3, g1, g2, g3, r33, g43, g53 ;
+
+   /*** acoustic tube parameters for current segment ***/
+    an = Pi * diam * diam / 4. ;                /* cross-sectional area of tube (cm2) */
+    d = seglength;                              /* length of tube (cm) */ 
+    c = velocity ;                              /* sound velocity in air (cm/s) */
+    alpha = attn ;                              /* attenuation constant of travelling waves (1/cm) */
+
+   /*** sampling frequency ***/
+    fs2 = samplerate * 2. ;
+
+   /*** electrical circuit elements (CGS units) for current segment ***/
+    Ln = ( rho * d ) / an ;                     /* series inductor (either Lch or Lcl) */
+    Cn = ( an * d ) / ( rho * c * c ) ;         /* shunt capacitor (either Cch or Ccl) */
+    Zn = sqrt( Ln / Cn ) ;                      /* characteristic impedance ( either Zch or Zcl) */
+    Gn = 2. / Zn * alpha * d ;                  /* shunt conductance (either Gch or Gcl) */
+
+   /*** WDF multiplier coefficients for current tube segment ***/
+
+    /* Adaptor 3 */
+    r1 = fs2 * Ln / 2. ;                                        /* 0.5 Zl */
+    r2 = rn_1 ;
+    r3 = r33 = r1 + r2 ;
+    filter_state->gamma31 = r1 / r3 ;
+
+    /* Adaptor 5 */
+    g1 = Gn ;                                                   /* (1 / Zg ) */
+    g2 = fs2 * Cn ;                                             /* (1 / Zc ) */
+    g3 = g53 = g1 + g2 ;
+    filter_state->gamma51 = g1 / g3 ;
+
+    /* Adaptor 4 */
+    g1 = 1 / r33 ;
+    g2 = g53 ;
+    g3 = g43 = g1 + g2 ;
+    filter_state->gamma41 = g1 / g3 ;
+
+    /* Adaptor 6 */
+    r1 = 1 / g43 ;
+    r2 = fs2 * Ln / 2. ;                                        /* 0.5 Zl */
+    r3 = rn_1 = r1 + r2 ;
+    filter_state->gamma61 = r1 / r3 ;
+
+#ifdef _DEBUG_
+    fprintf( stderr, "gamma31=%f ", filter_state->gamma31 ) ;
+    fprintf( stderr, "gamma41=%f ", filter_state->gamma41 ) ;
+    fprintf( stderr, "gamma51=%f ", filter_state->gamma51 ) ;                                            
+    fprintf( stderr, "gamma61=%f\n", filter_state->gamma61 ) ;  
+#endif
+
+   /*** initialise filter states ***/
+    filter_state->Nstates = N_OF_EARTUBE_STATES ;
+    filter_state->states =  ( char * ) NewZeroedArray( StateType, filter_state->Nstates,
+						       "wdf_ear.c for earcanal states\n" ) ;
+
+   /*** return ***/
+    return( filter_state ) ;
+}
+
+
+/********************** EarmiddleWDF() ***************************/
+
+EarmiddleWDFstate *EarmiddleWDF( samplerate, zov, output_scale, ratio )
+    double  samplerate, zov, output_scale, ratio ;
+{
+    DeclareNew( EarmiddleWDFstate *, filter_state ) ;
+    double  fs2 ;
+    double  r1, r2, r3, r4, g1, g2, g3, g4 ;
+    double  r73, r84, r94, r103, r114, r123, r133, r143, r153 ;
+    double  r163, r174, r183, r193, r204, r214 ;
+    void    update_earmiddle_init(), update_earmiddle() ;
+
+   /*** sampling frequency ***/
+    fs2 = samplerate * 2. ;
+
+   /*** WDF-port resistances and multiplier coefficients ***/
+ 
+    /* Adaptor 9 */
+    r1 = 1. / ( fs2 * Cp ) ;                                    /* Zcp */
+    r2 = Ra ;                                                   /* Zra */
+    r3 = fs2 * La ;                                             /* Zla */
+    r4 = r94 = r1 + r2 + r3 ;
+    filter_state->gamma91 = r1 / r4 ;
+    filter_state->gamma92 = r2 / r4 ;
+
+    /* Adaptor 8 */
+    g1 = 1. / r94 ;
+    g2 = 1. / Rm ;                                              /* (1 / Zrm) */
+    g3 = fs2 * Ct ;                                             /* (1 / Zct) */
+    g4 = g1 + g2 + g3 ;
+    r84 = 1. /g4 ;
+    filter_state->gamma81 = g1 / g4 ;
+    filter_state->gamma82 = g2 / g4 ;
+
+    /* Adaptor 7 */
+    r1 = r84 ;
+    r2 = rn_1 ;
+    r3 = r73 = r1 + r2 ;
+    filter_state->gamma71 = r1 / r3 ;
+
+    /* Adaptor 13 */
+    r1 = 1. / ( fs2 * Cd2 ) ;                                   /* Zcd2 */
+    r2 = Rd2 ;                                                  /* Zrd2 */
+    r3 = r133 = r1 + r2 ;
+    filter_state->gamma131 = r1 / r3 ;
+
+    /* Adaptor 12 */
+    g1 = 1. / ( fs2 * Ld ) ;                                    /* (1 / Zld) */
+    g2 = 1. / r133 ;
+    g3 = g1 + g2 ;
+    r123 = 1. / g3 ;
+    filter_state->gamma121 = g1 / g3 ;
+
+    /* Adaptor 14 */
+    g1 = 1. / Rd3 ;                                             /* (1 / Zrd3) */
+    g2 = fs2 * Cd3 ;                                            /* (1 / Zcd3) */
+    g3 = g1 + g2 ;
+    r143 = 1. / g3 ;
+    filter_state->gamma141 = g1 / g3 ;
+
+    /* Adaptor 15 */
+    r1 = 1. / ( fs2 * Cd1 ) ;                                   /* Zcd1 */
+    r2 = Rd1 ;                                                  /* Zrd1 */
+    r3 = r153 = r1 + r2 ;
+    filter_state->gamma151 = r1 / r3 ;
+
+    /* Adaptor 11 */
+    r1 = r143 ;
+    r2 = r123 ;
+    r3 = r153 ;
+    r4 = r114 = r1 + r2 + r3 ;
+    filter_state->gamma111 = r1 / r4 ;
+    filter_state->gamma112 = r2 / r4 ;
+
+    /* Adaptor 10 */
+    g1 = 1. / r114 ;
+    g2 = 1. / r73 ;
+    g3 = g1 + g2 ;
+    r103 = 1. /g3 ;
+    filter_state->gamma101 = g1 / g3 ;
+ 
+    /* Adaptor 17 */
+    r1 = 1. / ( fs2 * Co ) ;                                    /* Zco */
+    r2 = Ro ;                                                   /* Zro */
+    r3 = fs2 * Lo ;                                             /* Zlo */
+    r4 = r174 = r1 + r2 + r3 ;
+    filter_state->gamma171 = r1 / r4 ;
+    filter_state->gamma172 = r2 / r4 ;
+
+    /* Adaptor 16 */
+    r1 = r103 ;
+    r2 = r174 ;
+    r3 = r163 = r1 + r2 ;
+    filter_state->gamma161 = r1 / r3 ;
+
+    /* Adaptor 19 */
+    r1 = 1. / ( fs2 * Cs ) ;                                    /* Zcs */
+    r2 = Rs ;                                                   /* Zrs */
+    r3 = r193 = r1 + r2 ;
+    filter_state->gamma191 = r1 / r3 ;
+
+    /* Adaptor 18 */
+    g1 = 1. / r163 ;
+    g2 = 1. / r193 ;
+    g3 = g1 + g2 ;
+    r183 = 1. / g3 ;
+    filter_state->gamma181 = g1 / g3 ;
+
+    /* Adaptor 20 */
+#ifdef _DEBUG_
+    fprintf( stderr, "Zov=%e\n", zov ) ;
+#endif
+    if( zov != 0. ) {                                           /* coupled version ? */
+      r1 = Ral ;                                                /* Zral */
+      r2 = zov / ( ratio * ratio ) ;                            /* Zov / (r*r) */
+    }
+    else {                                                      /* uncoupled version */
+      r1 = 0.0 ;
+      r2 = Rc ;                                                 /* Zrc */
+    }
+    r3 = r183 ;
+    r4 = r204 = r1 + r2 +r3 ;
+    filter_state->gamma201 = r1 / r4 ;
+    filter_state->gamma202 = r2 / r4 ;
+    filter_state->ratio = ratio ;
+
+    /* Adaptor 21 */
+    r1 = 1. / ( fs2 * Cc ) ;                                    /* Zcc */
+    r2 = r204 ;
+    if( zov != 0. )                                             /* coupled version ? */
+      r3 = 0.0 ;
+    else                                                        /* uncoupled version */
+      r3 = fs2 * Lc ;                                           /* Zlc */
+    r4 = r214 = r1 + r2 + r3 ;
+    filter_state->gamma211 = r1 / r4 ;
+    filter_state->gamma212 = r2 / r4 ;
+    
+    /* Adaptor 22 */
+    r1 = Kst_ratio * Kst_max / fs2 ;                            /* Zcst (initial value only) */
+    r2 = r214 ;
+    r3 = r1 + r2 ;
+    filter_state->gamma221 = 2. * r1 / r3 ;
+
+#ifdef _DEBUG_
+    fprintf ( stderr, "gamma71 =%f            \n", filter_state->gamma71  ) ;
+    fprintf ( stderr, "gamma81 =%f gamma82 =%f\n", filter_state->gamma81
+						 , filter_state->gamma82  ) ;
+    fprintf ( stderr, "gamma91 =%f gamma92 =%f\n", filter_state->gamma91
+						 , filter_state->gamma92  ) ;
+    fprintf ( stderr, "gamma101=%f            \n", filter_state->gamma101 ) ;
+    fprintf ( stderr, "gamma111=%f gamma112=%f\n", filter_state->gamma111
+						 , filter_state->gamma112 ) ;
+    fprintf ( stderr, "gamma121=%f            \n", filter_state->gamma121 ) ;
+    fprintf ( stderr, "gamma131=%f            \n", filter_state->gamma131 ) ;
+    fprintf ( stderr, "gamma141=%f            \n", filter_state->gamma141 ) ;
+    fprintf ( stderr, "gamma151=%f            \n", filter_state->gamma151 ) ;
+    fprintf ( stderr, "gamma161=%f            \n", filter_state->gamma161 ) ;
+    fprintf ( stderr, "gamma171=%f gamma172=%f\n", filter_state->gamma171
+						 , filter_state->gamma172 ) ;
+    fprintf ( stderr, "gamma181=%f            \n", filter_state->gamma181 ) ;
+    fprintf ( stderr, "gamma191=%f            \n", filter_state->gamma191 ) ;
+    fprintf ( stderr, "gamma201=%f gamma202=%f\n", filter_state->gamma201
+						 , filter_state->gamma202 ) ;
+    fprintf ( stderr, "gamma211=%f gamma212=%f\n", filter_state->gamma211
+						 , filter_state->gamma212 ) ;
+    fprintf ( stderr, "gamma221=%f            \n", filter_state->gamma221 ) ;
+#endif  
+
+   /*** specify and initialise coefficient update procedure ***/
+    filter_state->update_proc = update_earmiddle ; 
+    ( void ) update_earmiddle_init( fs2, r214 ) ;
+
+   /*** output scaling ***/ 
+    filter_state->out_scale = output_scale ;
+
+   /*** initialise filter states ***/
+    filter_state->Nstates = N_OF_EARMIDDLE_STATES ;
+    filter_state->states = ( char * ) NewZeroedArray( StateType, filter_state->Nstates, 
+						      "wdf_ear.c for middle ear states\n" ) ;
+
+   /*** return ***/
+    return( filter_state ) ;
+}
+
+
+/*********************************** WDF procedures ***************************************
+* name:                 function:
+*
+* DoEarWDF()            WDF realization of the _UNCOUPLED_ version of the outer ear and 
+*                       middle ear networks. It is called by function ``ear_callback()'' 
+*                       in file ``ear.c'' once for a specified number of input points. 
+*                       It computes the stapes velocity for each input point and keeps 
+*                       track of the filter states.  
+*
+*                       Note: The code for the realization of the _COUPLED_ ear version         
+*                             is located in file ``wdf_tl.c''.
+*
+*
+* CloseEarWDF()         Deletes all private data structures and arrays of the outer and 
+*                       middle ear filter upon completion of filtering. 
+*                       In the case of the _UNCOUPLED_ ear version, it is called by the 
+*                       function ``ear_callback()'' in file ``ear.c''.
+*                       In the case of the _COUPLED_ ear version, it is called by the 
+*                       function ``tlf_bank_callbank()'' in file ``bank_tl.c''.
+******************************************************************************************/
+
+/******************************* DoEarWDF() **************************************/
+
+void DoEarWDF( ws, eartube_states, ms, tube_segments, inout_ptr, points )
+    register WaveWDFstate       *ws ;
+    register EartubeWDFstate    **eartube_states ;
+    register EarmiddleWDFstate  *ms ;
+    register int                tube_segments ;
+    register DataType           *inout_ptr ;
+    register int                points ;
+{
+    register EartubeWDFstate    *ts ;
+    register StateType          *st, in, out ;
+    register StateType          a0, an_1, b1, b2, b3, b161, b202, b212, bn ;
+    register int                tube_segment = -1 ;
+#ifdef _IMPULSE_
+    static   int                impulse = 10000 ;
+#endif
+
+
+    /***** update stapedial muscle state *****/
+
+     ( void ) ms->update_proc( ms ) ;
+
+    /***** for all points *****/
+
+     while( points-- > 0 ) {
+
+     /*** input point ***/
+
+	in = ( StateType ) *inout_ptr ;
+
+#ifdef _IMPULSE_
+	if( impulse != 0 )  {
+	   in = ( StateType ) impulse ;
+	   impulse = 0 ;
+	}
+	else 
+	   in = ( StateType ) 0 ;
+#endif
+
+     /*** computation 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 = 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[3] = -st[9] ;                                     /* b204 = a212 */
+
+	/* Adaptor 21 */
+	st[0] = st[2] - st[1] - st[3] ;                      /* b214 = a222 */
+
+
+     /*** computation 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 = - 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 */
+
+     /*** output ***/
+#ifdef _EARDRUM_
+	st = ( StateType * ) ms->states ;      
+	out = 0.5 * ( st[39] + an_1 ) * ms->out_scale ; 
+#else
+	out = -0.5 * b202 * ms->out_scale ; 
+#endif
+
+#ifdef _OVERFLOW_
+        if( out > _MaxOutput_ || out < _MinOutput_ )
+	   fprintf( stderr, "Overflow error in Outer/Middle ear output=%e\n", ( double ) out ) ;
+#endif     
+	*inout_ptr++ = out ; 
+
+    }
+    return ;
+}
+
+
+/******************************* CloseEarWDF() *************************************/
+
+void CloseEarWDF( wave_states, eartube_states, earmiddle_states, tube_segments )
+  register WaveWDFstate       *wave_states ;
+  register EartubeWDFstate    **eartube_states ;
+  register EarmiddleWDFstate  *earmiddle_states ;
+  register int                tube_segments ;
+{
+  Delete( earmiddle_states->states ) ;
+  Delete( earmiddle_states ) ;
+
+  for( ; --tube_segments < 0 ; ) {
+     Delete( eartube_states[ tube_segments ]->states ) ;
+     Delete( eartube_states[ tube_segments ] ) ;
+  }
+  Delete( eartube_states ) ; 
+
+  Delete( wave_states->states ) ;
+  Delete( wave_states ) ;
+
+  return ;
+
+}
+
+/************** low level functions **************/
+
+static double den221 ;
+static double Kst_old_ratio ; 
+
+void update_earmiddle_init( rate, z )
+  double rate, z ;
+{
+  Kst_old_ratio = Kst_ratio ; 
+  den221 = rate * z / Kst_max ;
+  return ;
+}
+
+void update_earmiddle( ms )
+  EarmiddleWDFstate *ms ;
+{
+  if( Kst_ratio < Kst_min_ratio )
+    Kst_ratio = Kst_min_ratio ;
+
+  ms->gamma221 = 2. * Kst_ratio / ( Kst_ratio + den221 ) ;
+  ms->states[4] = ms->states[4] * Kst_ratio / Kst_old_ratio ;
+
+  Kst_old_ratio = Kst_ratio ;
+ 
+  return ;
+}
+