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