tomwalters@0: /* tomwalters@0: Copyright (c) Applied Psychology Unit, Medical Research Council. 1988, 1989 tomwalters@0: =========================================================================== tomwalters@0: tomwalters@0: Permission to use, copy, modify, and distribute this software without fee tomwalters@0: is hereby granted for research purposes, provided that this copyright tomwalters@0: notice appears in all copies and in all supporting documentation, and that tomwalters@0: the software is not redistributed for any fee (except for a nominal shipping tomwalters@0: charge). Anyone wanting to incorporate all or part of this software in a tomwalters@0: commercial product must obtain a license from the Medical Research Council. tomwalters@0: tomwalters@0: The MRC makes no representations about the suitability of this tomwalters@0: software for any purpose. It is provided "as is" without express or implied tomwalters@0: warranty. tomwalters@0: tomwalters@0: THE MRC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING tomwalters@0: ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE tomwalters@0: A.P.U. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY tomwalters@0: DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN tomwalters@0: AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF tomwalters@0: OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. tomwalters@0: */ tomwalters@0: tomwalters@0: /* tomwalters@0: Acknowledgment: tomwalters@0: ============== tomwalters@0: tomwalters@0: The source code provided in this file was originally developed by tomwalters@0: Christian Giguere as part of a Ph.D degree at the Department of tomwalters@0: Engineering of the University of Cambridge from April 1990 to tomwalters@0: November 1993. The code was subsequently adapted under a grant tomwalters@0: from the Hearing Research Trust for full compatibility with tomwalters@0: AIM Release 6.15. tomwalters@0: tomwalters@0: Christian Giguere 25/03/94 tomwalters@0: tomwalters@0: */ tomwalters@0: tomwalters@0: /* tomwalters@0: =========================================================== tomwalters@0: meddis.c tomwalters@0: =========================================================== tomwalters@0: tomwalters@0: Implementation of Ray Meddis haircell model. tomwalters@0: tomwalters@0: Author : Christian Giguere tomwalters@0: First written : 10th September, 1991 tomwalters@0: Last edited : 07th March, 1994 tomwalters@0: tomwalters@0: References: tomwalters@0: (1) C.Giguere and P.C.Woodland (1994). JASA 95(1): 331-342. tomwalters@0: (2) R.Meddis et al. (1990). JASA 87(4): 1813-1816. tomwalters@0: (3) R.Meddis (1988). JASA 83(3): 1056-1063. tomwalters@0: tomwalters@0: Note - This is a complete re-write of the original file tomwalters@0: "haircell.c" Release 3 (20th September 1988) from tomwalters@0: J. Holdsworth and the Applied Psychology Unit. tomwalters@0: ============================================================ tomwalters@0: */ tomwalters@0: tomwalters@0: tomwalters@0: /***** includes *****/ tomwalters@0: tomwalters@0: #include tomwalters@0: #include tomwalters@0: #include "stitch.h" tomwalters@0: #include "source.h" tomwalters@0: #include "calc.h" tomwalters@0: #include "calc_tl.h" tomwalters@0: #include "meddis.h" tomwalters@0: tomwalters@0: tomwalters@0: /***** defines *****/ tomwalters@0: tomwalters@0: #if 0 tomwalters@0: #define _DEBUG_ tomwalters@0: #define _OVERFLOW_ tomwalters@0: #endif tomwalters@0: tomwalters@0: #if 1 /* define numerical implementation */ tomwalters@0: #define _WDF_ /* Giguere and Woodland (1994) */ tomwalters@0: #else tomwalters@0: #define _FORWARD_DIFFERENCE_ /* Meddis et al. (1990) */ tomwalters@0: #endif tomwalters@0: tomwalters@0: #if 0 tomwalters@0: #define _CLAMPING_ /* clamping of reservoirs */ tomwalters@0: #endif tomwalters@0: tomwalters@0: #define NUMBER_OF_COEFFS ( 12 ) tomwalters@0: #define NUMBER_OF_STATES ( 12 ) tomwalters@0: tomwalters@0: tomwalters@0: /* Feedback parameter */ tomwalters@0: tomwalters@0: #define InputGain_max ( 24.0 ) /* max coupling gain in dB */ tomwalters@0: tomwalters@0: tomwalters@0: /* Meddis et al. (1990) model parameters */ tomwalters@0: tomwalters@0: #define A_medium ( 10.0 ) /* medium-spontaneous rate fibre */ tomwalters@0: #define B_medium ( 3000.0 ) tomwalters@0: #define g_medium ( 1000.0 ) tomwalters@0: tomwalters@0: #define A_high ( 5.0 ) /* high-spontaneous rate fibre */ tomwalters@0: #define B_high ( 300.0 ) tomwalters@0: #define g_high ( 2000.0 ) tomwalters@0: tomwalters@0: #define y_value ( 5.05 ) /* replenishment rate */ tomwalters@0: #define l_value ( 2500 ) /* rate of loss from the cleft */ tomwalters@0: #define r_value ( 6580 ) /* rate of return from the cleft */ tomwalters@0: #define x_value ( 66.31 ) /* rate of release from w store */ tomwalters@0: #define m_value ( 1.0 ) /* max number of quanta */ tomwalters@0: #define h_value ( 50000. ) /* firing-rate factor */ tomwalters@0: tomwalters@0: tomwalters@0: /***** private data structures *****/ tomwalters@0: tomwalters@0: typedef struct _haircell_info HaircellInfo ; tomwalters@0: typedef struct _haircell_bank HaircellBank ; tomwalters@0: tomwalters@0: struct _haircell_info { tomwalters@0: int number_of_states ; tomwalters@0: double output_gain ; tomwalters@0: StateType *states ; tomwalters@0: } ; tomwalters@0: tomwalters@0: struct _haircell_bank { tomwalters@0: int channels ; tomwalters@0: double *inputGain_ratio ; tomwalters@0: double (*update_proc)() ; /* procedure to update cells input gain */ tomwalters@0: int number_of_coeffs ; tomwalters@0: CoeffType *coeffs ; tomwalters@0: HaircellInfo **cells ; tomwalters@0: } ; tomwalters@0: tomwalters@0: tomwalters@0: /***** external variables *****/ tomwalters@0: tomwalters@0: double *InputGain_ratio ; tomwalters@0: tomwalters@0: tomwalters@0: /***** functions *****/ tomwalters@0: tomwalters@0: /******************************************************************************* tomwalters@0: * name: function: tomwalters@0: * tomwalters@0: * NewHaircells() Set-up function for the implementation of a bank of inner tomwalters@0: * haircells based on the model of Ray Meddis. It generates the tomwalters@0: * multiplier coefficients for each haircell filter and tomwalters@0: * initializes the filter states. It is called by function tomwalters@0: * ``Meddis()'' in file ``model.c''. It returns a pointer to a tomwalters@0: * structure containing all the relevant information for the tomwalters@0: * computation of the haircell filters. tomwalters@0: * tomwalters@0: * The choice of the haircell type (medium or high-spontaneous tomwalters@0: * rate fibre) is made at run time using options ``fibre_med''. tomwalters@0: * tomwalters@0: * The choice of the numerical implementation (Wave Digital tomwalters@0: * Filtering or Forward Difference algorithm) is made at tomwalters@0: * compile time using a macro substitution ``#define'' (see above). tomwalters@0: * tomwalters@0: * The choice of clamping the reservoirs' contents to non- tomwalters@0: * negative values is made at compile time using a macro tomwalters@0: * substitution ``#define'' (see above). tomwalters@0: * tomwalters@0: * DoHaircells() Realization of the bank of haircell filters. It computes the tomwalters@0: * output for each haircell for a specified number of input tomwalters@0: * points and keeps track of the filters' states. It returns tomwalters@0: * the size of the output data in bytes (per point). tomwalters@0: * tomwalters@0: * CloseHaircells() Deletes all private data structures and arrays of the Haircell tomwalters@0: * filters upon comletion of filtering. tomwalters@0: ********************************************************************************/ tomwalters@0: tomwalters@0: tomwalters@0: /*************************** NewHaircells() *********************************/ tomwalters@0: tomwalters@0: Pointer NewHaircells( channels, samplerate, output_gain, coupling, fibreType ) tomwalters@0: int channels ; tomwalters@0: double samplerate, output_gain ; tomwalters@0: double coupling ; tomwalters@0: char *fibreType ; tomwalters@0: { tomwalters@0: DeclareNew( HaircellBank *, bank ) ; tomwalters@0: HaircellInfo *cell_info ; tomwalters@0: CoeffType *cf ; tomwalters@0: StateType *st ; tomwalters@0: double ydt, ldt, rdt, xdt, gdt ; tomwalters@0: double kdt_init, q_init, c_init, w_init, cell_scaling ; tomwalters@0: double A_value, B_value, g_value ; tomwalters@0: double r1, r2, r3, r4, r03 ; tomwalters@0: double update_inputGain() ; tomwalters@0: int channel ; tomwalters@0: tomwalters@0: /*** scaling of input data ***/ tomwalters@0: cell_scaling = coupling ; tomwalters@0: tomwalters@0: /*** allocate fibre-dependent haircell parameters ***/ tomwalters@0: if( strncmp( fibreType, "high", 3 ) == 0 ) { tomwalters@0: A_value = A_high ; tomwalters@0: B_value = B_high ; tomwalters@0: g_value = g_high ; tomwalters@0: } tomwalters@0: else { tomwalters@0: A_value = A_medium ; tomwalters@0: B_value = B_medium ; tomwalters@0: g_value = g_medium ; tomwalters@0: } tomwalters@0: tomwalters@0: #ifdef _DEBUG_ tomwalters@0: fprintf( stderr, "Meddis Haircell fibre type=%s\n", fibreType ) ; tomwalters@0: fprintf( stderr, "A=%.2f B=%.2f g=%.2f\n", A_value, B_value, g_value ) ; tomwalters@0: fprintf( stderr, "Input scaling factor=%.4f\n", cell_scaling ) ; tomwalters@0: #endif tomwalters@0: tomwalters@0: tomwalters@0: /*** scale model parameters for difference equations ***/ tomwalters@0: ydt = y_value / samplerate ; tomwalters@0: ldt = l_value / samplerate ; tomwalters@0: rdt = r_value / samplerate ; tomwalters@0: xdt = x_value / samplerate ; tomwalters@0: gdt = g_value / samplerate ; tomwalters@0: tomwalters@0: tomwalters@0: /*** calculate initial values for infinite history of silence ***/ tomwalters@0: kdt_init = gdt * A_value / ( A_value + B_value ) ; tomwalters@0: q_init = m_value / ( 1. + kdt_init / ydt * ( 1. - rdt / ( ldt + rdt ) ) ) ; tomwalters@0: c_init = q_init * kdt_init / ( ldt + rdt ) ; tomwalters@0: w_init = rdt / xdt * c_init ; tomwalters@0: tomwalters@0: tomwalters@0: /*** allocate and store multiplier coefficients ***/ tomwalters@0: bank->number_of_coeffs = NUMBER_OF_COEFFS ; tomwalters@0: cf = bank->coeffs = NewArray( CoeffType, bank->number_of_coeffs, tomwalters@0: "haircell_tl.c for coefficients" ) ; tomwalters@0: #ifdef _WDF_ tomwalters@0: output_gain = MEDDIS_SCALE * output_gain * h_value / ( 2. * r_value ) ; tomwalters@0: cf[0] = m_value * y_value * output_gain ; /* M/Zy (normalized) */ tomwalters@0: cf[1] = A_value / cell_scaling ; tomwalters@0: cf[2] = ( A_value + B_value ) / cell_scaling ; tomwalters@0: cf[3] = g_value ; tomwalters@0: tomwalters@0: /* Adaptor B0 */ tomwalters@0: r1 = y_value ; /* ( 1/Zy ) */ tomwalters@0: r2 = 2. * samplerate ; /* ( 1/Zcq ) */ tomwalters@0: r3 = r03 = r1 + r2 ; tomwalters@0: cf[5] = r1 / r3 ; /* gamma01 */ tomwalters@0: cf[4] = r3 / cf[3] ; /* needed to update gamma11 */ tomwalters@0: tomwalters@0: /* Adaptor B1*/ tomwalters@0: r1 = kdt_init * samplerate ; /* ( 1/Zk ) initial value only */ tomwalters@0: r2 = r03 ; tomwalters@0: r3 = r1 + r2 ; tomwalters@0: cf[6] = 2. * r1 / r3 ; /* gamma11 updated at each sample */ tomwalters@0: tomwalters@0: /* Adaptor B2 */ tomwalters@0: r1 = l_value ; /* ( 1/Zl ) */ tomwalters@0: r2 = r_value ; /* ( 1/Zr ) */ tomwalters@0: r3 = 2. * samplerate ; /* ( 1/Zcc ) */ tomwalters@0: r4 = r1 + r2 + r3 ; tomwalters@0: cf[7] = r1 / r4 ; /* gamma21 */ tomwalters@0: cf[8] = r2 / r4 ; /* gamma22 */ tomwalters@0: tomwalters@0: /* Adaptor B3 */ tomwalters@0: r1 = x_value ; /* ( 1/Zx ) */ tomwalters@0: r2 = 2. * samplerate ; /* ( 1/Zcw ) */ tomwalters@0: r3 = r1 + r2 ; tomwalters@0: cf[9] = 0.5 * r1 / r3 ; /* 0.5 * gamma31 */ tomwalters@0: tomwalters@0: #else tomwalters@0: output_gain = MEDDIS_SCALE * output_gain * h_value ; tomwalters@0: cf[0] = m_value * output_gain ; tomwalters@0: cf[1] = A_value / cell_scaling ; tomwalters@0: cf[2] = ( A_value + B_value ) / cell_scaling ; tomwalters@0: cf[3] = gdt ; tomwalters@0: cf[4] = ydt ; tomwalters@0: cf[5] = ldt ; tomwalters@0: cf[6] = rdt ; tomwalters@0: cf[7] = xdt ; tomwalters@0: #endif tomwalters@0: tomwalters@0: tomwalters@0: /*** loop through each haircell ***/ tomwalters@0: bank->channels = channels ; tomwalters@0: bank->cells = NewArray( HaircellInfo *, bank->channels, "haircell.c for cell states" ) ; tomwalters@0: bank->inputGain_ratio = InputGain_ratio = NewArray( double, bank->channels, tomwalters@0: "haircell_tl.c for cells input gain ratio" ) ; tomwalters@0: bank->update_proc = update_inputGain ; tomwalters@0: tomwalters@0: for( channel = 0 ; channel < bank->channels ; channel++ ) { tomwalters@0: bank->inputGain_ratio[ channel ] = 0.5 ; /* in absence of feedback, Gain = 1 */ tomwalters@0: cell_info = bank->cells[ channel ] = New( HaircellInfo * ) ; tomwalters@0: tomwalters@0: /*** allocate and initialise states ***/ tomwalters@0: cell_info->number_of_states = NUMBER_OF_STATES ; tomwalters@0: st = cell_info->states = NewZeroedArray( StateType, cell_info->number_of_states, tomwalters@0: "haircell_tl.c for states" ) ; tomwalters@0: tomwalters@0: #ifdef _WDF_ tomwalters@0: st[1] = x_value * w_init * output_gain ; /* Ixn (normalized) */ tomwalters@0: st[2] = 2. * q_init * samplerate * output_gain ; /* Ixn(t-T)-b02 (normalized) */ tomwalters@0: st[3] = -2. * c_init * samplerate * output_gain ; /* b23 (normalized) */ tomwalters@0: st[4] = -2. * w_init * samplerate * output_gain ; /* b33 (normalized) */ tomwalters@0: cell_info->output_gain = output_gain ; /* obsolete */ tomwalters@0: tomwalters@0: #else tomwalters@0: st[0] = kdt_init ; tomwalters@0: st[1] = q_init * output_gain ; tomwalters@0: st[2] = c_init * output_gain ; tomwalters@0: st[3] = w_init * output_gain ; tomwalters@0: cell_info->output_gain = output_gain ; /* obsolete */ tomwalters@0: #endif tomwalters@0: tomwalters@0: } tomwalters@0: tomwalters@0: return( ( Pointer ) bank ) ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: /************************** DoHaircells() *********************************/ tomwalters@0: tomwalters@0: int DoHaircells( bank_ptr, bytes, output_ptr, end_output_ptr, input_ptr ) tomwalters@0: Pointer *bank_ptr ; tomwalters@0: ByteCount *bytes ; tomwalters@0: DataType *output_ptr, *end_output_ptr, *input_ptr ; tomwalters@0: { tomwalters@0: register HaircellBank *bank = ( HaircellBank * ) bank_ptr ; tomwalters@0: register HaircellInfo *cell_info ; tomwalters@0: register DataType *input, *output ; tomwalters@0: register StateType *st ; tomwalters@0: register StateType in, out ; tomwalters@0: register CoeffType *cf ; tomwalters@0: register double inputGain ; tomwalters@0: register int channel = bank->channels ; tomwalters@0: register int point, npoints = ( end_output_ptr - output_ptr ) / bank->channels ; tomwalters@0: #ifdef _WDF_ tomwalters@0: register StateType an, a0 ; tomwalters@0: #else tomwalters@0: register StateType replenish, eject, loss, reuptake, reprocess ; tomwalters@0: #endif tomwalters@0: tomwalters@0: /*** for all channels ***/ tomwalters@0: tomwalters@0: #ifdef _DEBUG_ tomwalters@0: fprintf( stderr, "DoHaircells() = %d points X %d channels\n", npoints, bank->channels ) ; tomwalters@0: #endif tomwalters@0: tomwalters@0: cf = bank->coeffs ; tomwalters@0: tomwalters@0: while( channel-- ) { tomwalters@0: tomwalters@0: cell_info = bank->cells[ channel ] ; tomwalters@0: st = cell_info->states ; tomwalters@0: input = input_ptr + channel ; tomwalters@0: output = output_ptr + channel ; tomwalters@0: inputGain = bank->update_proc( bank->inputGain_ratio[ channel ] ) ; tomwalters@0: cf[10] = cf[1] / inputGain ; /* (A/p) */ tomwalters@0: cf[11] = cf[2] / inputGain ; /* (A+B)/p */ tomwalters@0: tomwalters@0: /*** for each channel ***/ tomwalters@0: tomwalters@0: point = npoints ; tomwalters@0: while( point-- ) { tomwalters@0: tomwalters@0: #ifdef _WDF_ tomwalters@0: /*** compute compressive permeability function k ***/ tomwalters@0: in = ( StateType ) *input ; /* (BM vibration) */ tomwalters@0: if( in > -cf[10] ) tomwalters@0: st[0] = ( in + cf[10] ) / ( in + cf[11] ) ; /* kn(t) / g */ tomwalters@0: else tomwalters@0: st[0] = 0. ; tomwalters@0: tomwalters@0: /*** update gamma11 ***/ tomwalters@0: cf[6] = st[0] / ( st[0] + cf[4] ) ; tomwalters@0: cf[6] += cf[6] ; /* gamma11 */ tomwalters@0: tomwalters@0: /*** compute wdf ***/ tomwalters@0: tomwalters@0: /* Adaptor B0 */ tomwalters@0: st[5] = cf[0] + st[1] + st[2] ; /* -b03=a12 (normalized) */ tomwalters@0: tomwalters@0: /* Adaptor B1 */ tomwalters@0: st[6] = cf[6] * st[5] ; /* -b11=2Ikn (normalized) */ tomwalters@0: st[7] = st[6] - st[5] ; /* b12=-a03 (normalized) */ tomwalters@0: tomwalters@0: /* Adaptor B0 */ tomwalters@0: st[2] = cf[0] + st[1] - st[7] + cf[5] * ( st[7] - st[5] ) ; /* Ixn(t-T)-b02 (normalized) */ tomwalters@0: tomwalters@0: /* Adaptor B2 */ tomwalters@0: an = st[6] - st[3] ; /* a24 (normalized) */ tomwalters@0: a0 = an - st[3] ; /* a20 (normalized) */ tomwalters@0: st[8] = cf[8] * a0 ; /* -b22=2Irn (normalized) */ tomwalters@0: st[3] = cf[7] * a0 + st[8] - an ; /* b23 (normalized) */ tomwalters@0: tomwalters@0: /* Adaptor B3 */ tomwalters@0: an = st[8] - st[4] ; /* a33 (normalized) */ tomwalters@0: st[1] = cf[9] * ( an - st[4] ) ; /* Ixn(t)=-0.5*b31 (normalized) */ tomwalters@0: st[4] = st[1] + st[1] - an ; /* b32 (normalized) */ tomwalters@0: tomwalters@0: #ifdef _CLAMPING_ tomwalters@0: /*** clamping of reservoirs to non-negative quantities ***/ tomwalters@0: /*** not available yet ***/ tomwalters@0: #endif tomwalters@0: tomwalters@0: /*** output ***/ tomwalters@0: out = st[8] ; tomwalters@0: tomwalters@0: #else tomwalters@0: tomwalters@0: /*** compute change quantities ***/ tomwalters@0: replenish = cf[4] * ( cf[0] - st[1] ) ; /* y(M-q) */ tomwalters@0: eject = st[0] * st[1] ; /* kq */ tomwalters@0: loss = cf[5] * st[2] ; /* lc */ tomwalters@0: reuptake = cf[6] * st[2] ; /* rc */ tomwalters@0: reprocess = cf[7] * st[3] ; /* xw */ tomwalters@0: tomwalters@0: /*** compute compressive permeability function kn_1dt ***/ tomwalters@0: in = ( StateType ) *input ; /* (BM vibration) */ tomwalters@0: if( in > -cf[10] ) tomwalters@0: st[0] = cf[3] * ( in + cf[10] ) / ( in + cf[11] ) ; /* kn_1(t)dt */ tomwalters@0: else tomwalters@0: st[0] = 0. ; tomwalters@0: tomwalters@0: /*** update reservoir quantities ***/ tomwalters@0: st[1] += replenish - eject + reprocess ; /* q */ tomwalters@0: st[2] += eject - loss - reuptake ; /* c */ tomwalters@0: st[3] += reuptake - reprocess ; /* w */ tomwalters@0: tomwalters@0: #ifdef _CLAMPING_ tomwalters@0: /*** clamping of reservoirs to non-negative quantities ***/ tomwalters@0: if( st[1] < 0 ) tomwalters@0: st[1] = 0 ; tomwalters@0: if( st[2] < 0 ) tomwalters@0: st[2] = 0 ; tomwalters@0: #endif tomwalters@0: tomwalters@0: /*** output ***/ tomwalters@0: out = st[2] ; tomwalters@0: tomwalters@0: #endif tomwalters@0: tomwalters@0: /*** output ***/ tomwalters@0: #ifdef _OVERFLOW_ tomwalters@0: if( out > _MaxOutput_ ) tomwalters@0: fprintf( stderr, "Overflow error in Meddis Haircell output\%e\n", ( double ) out ) ; tomwalters@0: #endif tomwalters@0: tomwalters@0: *output = ( DataType ) out ; tomwalters@0: tomwalters@0: output += bank->channels ; tomwalters@0: input += bank->channels ; tomwalters@0: } tomwalters@0: tomwalters@0: } tomwalters@0: tomwalters@0: return( npoints ) ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: /************************** CloseHaircells() *********************************/ tomwalters@0: tomwalters@0: void CloseHaircells( bank_ptr ) tomwalters@0: Pointer bank_ptr ; tomwalters@0: { tomwalters@0: HaircellBank *bank = ( HaircellBank * ) bank_ptr ; tomwalters@0: HaircellInfo *cell_info ; tomwalters@0: int channel ; tomwalters@0: tomwalters@0: for( channel = 0 ; channel < bank->channels ; channel++) { tomwalters@0: Delete( bank->cells[ channel ]->states ) ; tomwalters@0: Delete( bank->cells[ channel ] ) ; tomwalters@0: } tomwalters@0: tomwalters@0: Delete( bank->cells ) ; tomwalters@0: Delete( bank->coeffs ) ; tomwalters@0: Delete( bank->inputGain_ratio ) ; tomwalters@0: Delete( bank ) ; tomwalters@0: tomwalters@0: return ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: /********** low level functions ************/ tomwalters@0: tomwalters@0: double update_inputGain( ratio ) tomwalters@0: double ratio ; tomwalters@0: { tomwalters@0: return( pow( 10., ( ratio - 0.5 ) * InputGain_max / 20. ) ) ; tomwalters@0: } tomwalters@0: