view 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 source
/*
    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 ;
}