Mercurial > hg > aim92
diff wdf/bank_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/bank_tl.c Fri May 20 15:19:45 2011 +0100 @@ -0,0 +1,499 @@ +/* + Copyright (c) Applied Psychology Unit, Medical Research Council. 1994 + =========================================================================== + + 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 + +*/ + +/* + =========================================================== + bank_tl.c + =========================================================== + + Design of cochlear transmission line filterbank (TLF) with + coupled outer ear and middle ear (EAR) filter. + + Author : Christian Giguere + First written : 01st April, 1991 + Last edited : 07th March, 1994 + + Reference: + (1) C.Giguere and P.C.Woodland (1994). JASA 95(1): 331-342. + =========================================================== +*/ + +/***** includes *****/ + +#include <math.h> +#include <stdio.h> +#include "stitch.h" +#include "source.h" +#include "calc.h" +#include "calc_tl.h" +#include "wdf_tl.h" +#include "formulae.h" +#include "formulae_tl.h" +#include "scales.h" +#include "scales_tl.h" +#include "bank.h" +#include "bank_tl.h" +#include "ear.h" +#include "wdf_ear.h" + +/***** defines *****/ + +#if 0 +#define _DEBUG_ +#endif + +#define MIN_CF ( 16.0 ) /* Minimum BM segment CF allowed (Hz) */ +#define MAX_CF ( 15000.0 ) /* Maximum BM segment CF allowed (Hz) */ +#define REF_CF ( 1000.0 ) /* Reference CF (Hz) for scaling BM output and Q factor */ +#define L ( 3.5 ) /* Length of BM (cm) */ +#define MinLength ( 1.0E-6 ) /* Mininum length of each BM segment (cm) */ +#define B0 ( 0.015 ) /* BM width at basal end (cm) */ +#define B_exp ( 0.30 ) /* Exponential constant for BM width (1/cm) */ +#define A0 ( 0.03 ) /* Cross-sectional area of each scala at basal end (cm2) */ +#define A_exp ( -0.60 ) /* Exponential constant for scalea cross-sectional area (1/cm) */ +#define RHO_fluid ( 1.0 ) /* Density of cochlear fluids [g/cm3] */ +#define MASS_PER_AREA ( 0.015 ) /* Transerval mass per area of basilar membrane (g/cm2) */ +#define Q_CHANNEL ( 0 ) /* Introduce channel-dependent Q factor */ +#define RATIO ( 30.0 ) /* Transformer ratio between oval window and eardrum */ +#define WARPING ( 0 ) /* Compensate frequency warping of bilinear transform */ + + +/***** externals */ + +extern Source InterpSource() ; + +/***** functions *****/ + +/************************************************************************** +* name: function: +* +* tlf_bank_callback() Callable procedure returning pointer to filtered +* data (BM velocity or displacement). +* +* TLF_GenBank() Set-up and initialization function for the design +* of the cochlear filterbank. Returns pointer to +* a new source. +***************************************************************************/ + +typedef struct _bank_segment BankSegment ; + +struct _bank_segment { + double center_frequency ; + double seglength, position ; + int active ; + } ; + +struct _tlf_bank_state { + struct _fillable_source parent ; + Source source ; + Pointer input ; + int inout_size ; + void (*proc)(), (*closeEar)(), (*closeTLF)() ; + int total_chans, display_chans ; + TLF_BankInfo *bank ; + WDFilterState **states ; + int Ntube ; + WaveWDFstate *wave_states ; + EartubeWDFstate **eartube_states ; + EarmiddleWDFstate *earmiddle_states ; + } ; + + +/************************* tlf_bank_callback() ****************************/ + +static Pointer tlf_bank_callback( state, bytes, buffer ) +struct _tlf_bank_state *state ; +ByteCount *bytes ; +Pointer buffer ; +{ + register TLF_BankInfo *bankInfo = state->bank ; + register int last = *bytes == 0 ; + register int points ; + register ByteCount bytecount ; + + /***** process *****/ + if( !last ) { + + points = *bytes * bankInfo->decimate_factor / + ( state->display_chans * state->inout_size ) ; + + state->input = Pull( state->source, points * state->inout_size ) ; + + state->proc( bankInfo, state->states, state->input, buffer, points, + state->total_chans, state->wave_states, state->eartube_states, + state->earmiddle_states, state->Ntube ) ; + + return ( buffer ) ; + } + + /***** close *****/ + else { + + Pull( state->source, 0 ) ; + state->closeEar( state->wave_states, state->eartube_states, state->earmiddle_states, state->Ntube ) ; + state->closeTLF( state->states, state->total_chans, state->bank ) ; + + return ( DeleteFillableSource( state ) ) ; + } +} + + +/************************** TLF_GenBank() *****************************/ + +Source TLF_GenBank( source, interps, info, chans, decimate_factor, samplerate, center_frequencies, + output_gain, outdens, qbase, OHC_gain, OHC_sat, motionstr, concha, canal ) +Source source ; +int interps, info, *chans, *decimate_factor ; +double samplerate, *center_frequencies, output_gain, outdens ; +double qbase, OHC_gain, OHC_sat ; +char *motionstr ; +TubeInfo *concha, *canal ; +{ + DeclareNew( struct _tlf_bank_state *, state ) ; + BankSegment **bankSeg, *segmentPointer, **GenerateBankSegments() ; + double density, freq, scale_disp, scale_vel ; + double qfactor, bn, xn, delta_xn, an, Lt, zov ; + double length, diam, attn ; + Source outputSource ; + int chan, total_chans, segment, counter = 0 ; + + /***** initialise input-related parameters *****/ + state->inout_size = sizeof ( DataType ) ; + state->input = ( char * ) 0 ; + state->source = source ; + + /***** set WDF-TLF filterbank parameters *****/ + density = DensityOnScale( ErbScale( center_frequencies[ 0 ]), ErbScale( center_frequencies[ *chans - 1]), + *chans ) ; + if( density == 0. ) { + if( outdens <= 0. ) + density = 1. ; /* arbitrary */ + else + density = outdens ; + } + + if( interps > 0 ) + interps = MIN( interps, ( int ) floor( log( ( double ) *chans ) / log( 2. ) ) ) ; + + bankSeg = GenerateBankSegments( interps, samplerate, center_frequencies, &density, &outdens, + chans, &total_chans ) ; + state->display_chans = *chans ; + state->total_chans = total_chans ; + state->states = NewArray( WDFilterState *, state->total_chans, "bank_tl.c for states" ) ; + + if( info ) + fprintf( stderr, "\nTLF filterbank information:\n" ) ; + + xn = bankSeg[0]->position ; + delta_xn = bankSeg[0]->seglength ; + Lt = -2. * RHO_fluid / ( A0 * A_exp ) * ( exp( -A_exp * L ) - exp( -A_exp * ( xn + delta_xn ) ) ) ; + + for( chan = 0 ; chan < state->total_chans ; chan++ ) { + segmentPointer = bankSeg[ chan ] ; + freq = segmentPointer->center_frequency ; + xn = segmentPointer->position ; + delta_xn = MAX( segmentPointer->seglength, MinLength ) ; + + /*** channel-dependent scalea cross-sectional area ***/ + an = A0 * exp( A_exp * xn ) ; + + /*** channel-dependent BM width ***/ + bn = B0 * exp( B_exp * xn ) ; + + /*** channel-dependent Q-factor ***/ + if ( Q_CHANNEL != 0 ) + qfactor = qbase * ( freq / REF_CF ) * ( Erb( REF_CF ) / Erb(freq) ) ; + else + qfactor = qbase ; + + /*** print filterbank configuration ***/ + if( info ) { + counter += 1 * segmentPointer->active ; + fprintf( stderr, "%3d -- active=%3d -- cf:%7.1f Hz =%6.2f ERBs -- x=%.3fcm delta=%.3fcm b=%.3fcm A=%.3fcm2\n", + state->total_chans - chan, counter * segmentPointer->active, freq, ErbScale( freq ), xn, delta_xn, bn, an ) ; + } + + /*** scale output ***/ + scale_vel = output_gain * FILTERBANK_SCALE ; + scale_disp = TwoPi * REF_CF * scale_vel ; + + /*** get filter states ***/ + state->states[ chan ] = WDFilter( samplerate, freq, scale_vel, scale_disp, RHO_fluid, an, bn, + qfactor, MASS_PER_AREA, delta_xn, Lt, WARPING, + segmentPointer->active, &zov, OHC_gain, OHC_sat ) ; + + Delete( segmentPointer ) ; + } + + Delete( bankSeg ) ; + + + /*** set parameters common to all channels ***/ + + state->bank = New( TLF_BankInfo * ) ; + state->bank->output_chans = state->display_chans ; + + /* set decimation parameters */ + state->bank->decimateCount = 0 ; + *decimate_factor = MAX( 1, *decimate_factor ) ; + state->bank->decimate_factor = *decimate_factor ; + + + /***** set output and nonlinearity variables, and filter procedure *****/ + + if( strncmp( motionstr, "velocity", 3 ) == 0 ) { + state->proc = DoWDFdataArray ; + state->bank->output_var = VELOCITY ; + state->bank->nl_var = DISPLACEMENT ; + } + else { + state->proc = DoWDFdataArray ; + state->bank->output_var = DISPLACEMENT ; + state->bank->nl_var = DISPLACEMENT ; + } + + + /***** Set WDF-EAR filter design parameters *****/ + state->wave_states = FreefieldWDF( samplerate, RHO_air, C, As, concha->diameter/2. ) ; + + state->Ntube = concha->Nsegments + canal->Nsegments ; + state->eartube_states = NewArray( EartubeWDFstate *, state->Ntube, "ear.c for eartube states" ) ; + + length = concha->length / concha->Nsegments ; + diam = concha->diameter ; + attn = concha->att_factor ; + for( segment = 0 ; segment < concha->Nsegments ; segment++ ) + state->eartube_states[ segment ] = EartubeWDF( samplerate, RHO_air, C, diam, length, attn ) ; + + length = canal->length / canal->Nsegments ; + diam = canal->diameter ; + attn = canal->att_factor ; + for( ; segment < state->Ntube ; segment++ ) + state->eartube_states[ segment ] = EartubeWDF( samplerate, RHO_air, C, diam, length, attn ) ; + + state->earmiddle_states = EarmiddleWDF( samplerate, zov, 1.0, RATIO ) ; + + + /***** specify procedures upon closing *****/ + state->closeEar = CloseEarWDF ; + state->closeTLF = CloseWDF ; + + /***** initialise output-related parameters and return *****/ + outputSource = SetFillableSource( state, tlf_bank_callback, "bank_tl.c filter" ) ; + + for( *chans = state->display_chans ; interps-- > 0 ; *chans = *chans * 2 - 1, density = density * 2. ) + outputSource = InterpSource( outputSource, *chans ) ; + + return ( outputSource ) ; +} + + +/************************** lower level functions *************************/ + +BankSegment **GenerateBankSegments( interps, samplerate, display_frequencies, display_dens, + out_dens, display_chans, total_chans ) +int interps ; +double samplerate ; +double *display_frequencies ; +double *display_dens, *out_dens ; +int *display_chans, *total_chans ; +{ + BankSegment **bankSeg, *segmentPointer ; + double min_cf, max_cf ; + double apical_cf, basal_cf ; + double *apical_frequencies, *basal_frequencies ; + double apical_dens, basal_dens, central_dens ; + int apical_chans, basal_chans, central_chans ; + int ichan, chan ; + int remainder ; + double cm_per_CB = GetERBscaling ( ) / 10. ; + + + /***** set number and density of central channels *****/ + if( interps >= 0 ) { + + central_chans = *display_chans = *display_chans >> interps ; + central_dens = *display_dens = ldexp( *display_dens, -interps ) ; + min_cf = FofErbScale( ErbScale( display_frequencies[0] ) - 1. / central_dens ) ; + max_cf = display_frequencies[ ( central_chans - 1 ) << interps ] ; + } + + else { + central_chans = *display_chans << -interps ; + central_dens = ldexp( *display_dens, -interps ) ; + min_cf = FofErbScale( ErbScale( display_frequencies[0] ) + - ( 1 << -interps ) / central_dens ) ; + max_cf = display_frequencies[*display_chans - 1] ; + } + + + /***** set number and density of apical and basal channels outside display range *****/ + + if( *out_dens <= 0. ) { + + apical_cf = min_cf ; + apical_dens = 0. ; + basal_cf = max_cf ; + basal_dens = 0. ; + } + else { + apical_cf = adjustCF( MIN_CF, samplerate ) ; + apical_dens = *out_dens ; + basal_cf = adjustCF( MAX_CF, samplerate ) ; + basal_dens = *out_dens ; + } + + apical_chans = 0 ; + basal_chans = 0 ; + + + /***** generate apical and basal center frequencies outside display range *****/ + + apical_frequencies = GenerateFrequencies( apical_cf, min_cf, min_cf, &apical_dens, &apical_chans ) ; + basal_frequencies = GenerateFrequencies( max_cf, basal_cf, max_cf, &basal_dens, &basal_chans ) ; + + /***** fill in array of BanKSegment structures with BM segment info *****/ + *total_chans = apical_chans + central_chans + basal_chans - 2 ; + bankSeg = NewArray( BankSegment *, *total_chans + 1, "tl_bank.c for segments" ) ; + + ichan = 0 ; + for( chan = 1 ; chan < apical_chans ; chan++ ) { + + bankSeg[ ichan++ ] = segmentPointer = New( BankSegment * ) ; + segmentPointer->center_frequency = apical_frequencies[ chan ] ; + segmentPointer->seglength = cm_per_CB / apical_dens ; + segmentPointer->position = L - cm_per_CB * ErbScale( segmentPointer->center_frequency ) ; + segmentPointer->active = 0 ; + } + + for( chan = 0 ; chan < central_chans ; chan++ ) { + + bankSeg[ ichan++ ] = segmentPointer = New( BankSegment * ) ; + + if( interps >= 0 ) { + segmentPointer->center_frequency = display_frequencies[ chan << interps ] ; + segmentPointer->active = 1 ; + } + else { + remainder = chan % ( 1 << -interps ) ; + segmentPointer->center_frequency = FofErbScale( ErbScale( display_frequencies[ chan >> -interps ] ) + + ( remainder - ( 1 << -interps ) + 1 ) / central_dens ) ; + segmentPointer->active = ( remainder == ( ( 1 << -interps ) - 1 ) ) ; + } + + segmentPointer->seglength = cm_per_CB / central_dens ; + segmentPointer->position = L - cm_per_CB * ErbScale( segmentPointer->center_frequency ) ; + } + + + for( chan = 1 ; chan < basal_chans ; chan++ ) { + + bankSeg[ ichan++ ] = segmentPointer = New( BankSegment * ) ; + segmentPointer->center_frequency = basal_frequencies[ chan ] ; + segmentPointer->seglength = cm_per_CB / basal_dens ; + segmentPointer->position = L - cm_per_CB * ErbScale( segmentPointer->center_frequency ) ; + segmentPointer->active = 0 ; + } + + segmentPointer = bankSeg[ 0 ] ; + if( ( segmentPointer->position + segmentPointer->seglength ) > L ) + segmentPointer->seglength = L - MIN( L, segmentPointer->position ) ; + + /***** return and deallocate dynamic memory *****/ + Delete( apical_frequencies ) ; + Delete( basal_frequencies ) ; + + return( bankSeg ) ; +} + + +double *GenerateFrequencies( min_cf, max_cf, base_cf, density, channels ) +double min_cf, max_cf, base_cf ; +double *density ; +int *channels ; +{ + double freq, *frequencies ; + + /*** map characteristic frequencies onto specified scale ***/ + min_cf = ErbScale( min_cf ) ; + max_cf = ErbScale( max_cf ) ; + max_cf = MAX( min_cf, max_cf ) ; /* max_cf cannot be smaller than min_cf */ + base_cf = ErbScale( base_cf ) ; + + + /*** call appropriate generating functions ***/ + if( *channels <= 0 ) { + + if( *density <= 0. ) { + *channels = 1 ; /* there must be at least one channel */ + freq = FofErbScale( min_cf ) ; /* arbitrary */ + frequencies = &freq ; + *density = 1. ; /* arbitrary */ + + } + else { + frequencies = GenerateScale( min_cf, max_cf, *density, base_cf, FofErbScale ) ; + *channels = NumberOnScale( min_cf, max_cf, *density, base_cf ) ; + } + } + + else { + frequencies = NumberedScale( min_cf, max_cf, *channels, FofErbScale ) ; + *density = DensityOnScale( min_cf, max_cf, *channels ) ; + if( *density == 0. ) + *density = 1. ; /* arbitrary */ + } + + /*** return pointer to array of center frequencies ***/ + return ( frequencies ) ; +} + + +double adjustCF( cf, samplerate ) +double cf, samplerate ; +{ + cf = MAX( cf, MIN_CF ) ; + cf = MAX( cf, FofErbScale( 0. ) ) ; + + cf = MIN( cf, samplerate / 2. ) ; + cf = MIN( cf, MAX_CF ) ; + cf = MIN( cf, FofErbScale( 10. * L / GetERBscaling() ) ); + + return ( cf ) ; +} +