comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:5242703e91d3
1 /*
2 Copyright (c) Applied Psychology Unit, Medical Research Council. 1994
3 ===========================================================================
4
5 Permission to use, copy, modify, and distribute this software without fee
6 is hereby granted for research purposes, provided that this copyright
7 notice appears in all copies and in all supporting documentation, and that
8 the software is not redistributed for any fee (except for a nominal shipping
9 charge). Anyone wanting to incorporate all or part of this software in a
10 commercial product must obtain a license from the Medical Research Council.
11
12 The MRC makes no representations about the suitability of this
13 software for any purpose. It is provided "as is" without express or implied
14 warranty.
15
16 THE MRC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
17 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE
18 A.P.U. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
19 DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
20 AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
21 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 */
23
24 /*
25 Acknowledgment:
26 ==============
27
28 The source code provided in this file was originally developed by
29 Christian Giguere as part of a Ph.D degree at the Department of
30 Engineering of the University of Cambridge from April 1990 to
31 November 1993. The code was subsequently adapted under a grant
32 from the Hearing Research Trust for full compatibility with
33 AIM Release 6.15.
34
35 Christian Giguere 25/03/94
36
37 */
38
39 /*
40 ===========================================================
41 bank_tl.c
42 ===========================================================
43
44 Design of cochlear transmission line filterbank (TLF) with
45 coupled outer ear and middle ear (EAR) filter.
46
47 Author : Christian Giguere
48 First written : 01st April, 1991
49 Last edited : 07th March, 1994
50
51 Reference:
52 (1) C.Giguere and P.C.Woodland (1994). JASA 95(1): 331-342.
53 ===========================================================
54 */
55
56 /***** includes *****/
57
58 #include <math.h>
59 #include <stdio.h>
60 #include "stitch.h"
61 #include "source.h"
62 #include "calc.h"
63 #include "calc_tl.h"
64 #include "wdf_tl.h"
65 #include "formulae.h"
66 #include "formulae_tl.h"
67 #include "scales.h"
68 #include "scales_tl.h"
69 #include "bank.h"
70 #include "bank_tl.h"
71 #include "ear.h"
72 #include "wdf_ear.h"
73
74 /***** defines *****/
75
76 #if 0
77 #define _DEBUG_
78 #endif
79
80 #define MIN_CF ( 16.0 ) /* Minimum BM segment CF allowed (Hz) */
81 #define MAX_CF ( 15000.0 ) /* Maximum BM segment CF allowed (Hz) */
82 #define REF_CF ( 1000.0 ) /* Reference CF (Hz) for scaling BM output and Q factor */
83 #define L ( 3.5 ) /* Length of BM (cm) */
84 #define MinLength ( 1.0E-6 ) /* Mininum length of each BM segment (cm) */
85 #define B0 ( 0.015 ) /* BM width at basal end (cm) */
86 #define B_exp ( 0.30 ) /* Exponential constant for BM width (1/cm) */
87 #define A0 ( 0.03 ) /* Cross-sectional area of each scala at basal end (cm2) */
88 #define A_exp ( -0.60 ) /* Exponential constant for scalea cross-sectional area (1/cm) */
89 #define RHO_fluid ( 1.0 ) /* Density of cochlear fluids [g/cm3] */
90 #define MASS_PER_AREA ( 0.015 ) /* Transerval mass per area of basilar membrane (g/cm2) */
91 #define Q_CHANNEL ( 0 ) /* Introduce channel-dependent Q factor */
92 #define RATIO ( 30.0 ) /* Transformer ratio between oval window and eardrum */
93 #define WARPING ( 0 ) /* Compensate frequency warping of bilinear transform */
94
95
96 /***** externals */
97
98 extern Source InterpSource() ;
99
100 /***** functions *****/
101
102 /**************************************************************************
103 * name: function:
104 *
105 * tlf_bank_callback() Callable procedure returning pointer to filtered
106 * data (BM velocity or displacement).
107 *
108 * TLF_GenBank() Set-up and initialization function for the design
109 * of the cochlear filterbank. Returns pointer to
110 * a new source.
111 ***************************************************************************/
112
113 typedef struct _bank_segment BankSegment ;
114
115 struct _bank_segment {
116 double center_frequency ;
117 double seglength, position ;
118 int active ;
119 } ;
120
121 struct _tlf_bank_state {
122 struct _fillable_source parent ;
123 Source source ;
124 Pointer input ;
125 int inout_size ;
126 void (*proc)(), (*closeEar)(), (*closeTLF)() ;
127 int total_chans, display_chans ;
128 TLF_BankInfo *bank ;
129 WDFilterState **states ;
130 int Ntube ;
131 WaveWDFstate *wave_states ;
132 EartubeWDFstate **eartube_states ;
133 EarmiddleWDFstate *earmiddle_states ;
134 } ;
135
136
137 /************************* tlf_bank_callback() ****************************/
138
139 static Pointer tlf_bank_callback( state, bytes, buffer )
140 struct _tlf_bank_state *state ;
141 ByteCount *bytes ;
142 Pointer buffer ;
143 {
144 register TLF_BankInfo *bankInfo = state->bank ;
145 register int last = *bytes == 0 ;
146 register int points ;
147 register ByteCount bytecount ;
148
149 /***** process *****/
150 if( !last ) {
151
152 points = *bytes * bankInfo->decimate_factor /
153 ( state->display_chans * state->inout_size ) ;
154
155 state->input = Pull( state->source, points * state->inout_size ) ;
156
157 state->proc( bankInfo, state->states, state->input, buffer, points,
158 state->total_chans, state->wave_states, state->eartube_states,
159 state->earmiddle_states, state->Ntube ) ;
160
161 return ( buffer ) ;
162 }
163
164 /***** close *****/
165 else {
166
167 Pull( state->source, 0 ) ;
168 state->closeEar( state->wave_states, state->eartube_states, state->earmiddle_states, state->Ntube ) ;
169 state->closeTLF( state->states, state->total_chans, state->bank ) ;
170
171 return ( DeleteFillableSource( state ) ) ;
172 }
173 }
174
175
176 /************************** TLF_GenBank() *****************************/
177
178 Source TLF_GenBank( source, interps, info, chans, decimate_factor, samplerate, center_frequencies,
179 output_gain, outdens, qbase, OHC_gain, OHC_sat, motionstr, concha, canal )
180 Source source ;
181 int interps, info, *chans, *decimate_factor ;
182 double samplerate, *center_frequencies, output_gain, outdens ;
183 double qbase, OHC_gain, OHC_sat ;
184 char *motionstr ;
185 TubeInfo *concha, *canal ;
186 {
187 DeclareNew( struct _tlf_bank_state *, state ) ;
188 BankSegment **bankSeg, *segmentPointer, **GenerateBankSegments() ;
189 double density, freq, scale_disp, scale_vel ;
190 double qfactor, bn, xn, delta_xn, an, Lt, zov ;
191 double length, diam, attn ;
192 Source outputSource ;
193 int chan, total_chans, segment, counter = 0 ;
194
195 /***** initialise input-related parameters *****/
196 state->inout_size = sizeof ( DataType ) ;
197 state->input = ( char * ) 0 ;
198 state->source = source ;
199
200 /***** set WDF-TLF filterbank parameters *****/
201 density = DensityOnScale( ErbScale( center_frequencies[ 0 ]), ErbScale( center_frequencies[ *chans - 1]),
202 *chans ) ;
203 if( density == 0. ) {
204 if( outdens <= 0. )
205 density = 1. ; /* arbitrary */
206 else
207 density = outdens ;
208 }
209
210 if( interps > 0 )
211 interps = MIN( interps, ( int ) floor( log( ( double ) *chans ) / log( 2. ) ) ) ;
212
213 bankSeg = GenerateBankSegments( interps, samplerate, center_frequencies, &density, &outdens,
214 chans, &total_chans ) ;
215 state->display_chans = *chans ;
216 state->total_chans = total_chans ;
217 state->states = NewArray( WDFilterState *, state->total_chans, "bank_tl.c for states" ) ;
218
219 if( info )
220 fprintf( stderr, "\nTLF filterbank information:\n" ) ;
221
222 xn = bankSeg[0]->position ;
223 delta_xn = bankSeg[0]->seglength ;
224 Lt = -2. * RHO_fluid / ( A0 * A_exp ) * ( exp( -A_exp * L ) - exp( -A_exp * ( xn + delta_xn ) ) ) ;
225
226 for( chan = 0 ; chan < state->total_chans ; chan++ ) {
227 segmentPointer = bankSeg[ chan ] ;
228 freq = segmentPointer->center_frequency ;
229 xn = segmentPointer->position ;
230 delta_xn = MAX( segmentPointer->seglength, MinLength ) ;
231
232 /*** channel-dependent scalea cross-sectional area ***/
233 an = A0 * exp( A_exp * xn ) ;
234
235 /*** channel-dependent BM width ***/
236 bn = B0 * exp( B_exp * xn ) ;
237
238 /*** channel-dependent Q-factor ***/
239 if ( Q_CHANNEL != 0 )
240 qfactor = qbase * ( freq / REF_CF ) * ( Erb( REF_CF ) / Erb(freq) ) ;
241 else
242 qfactor = qbase ;
243
244 /*** print filterbank configuration ***/
245 if( info ) {
246 counter += 1 * segmentPointer->active ;
247 fprintf( stderr, "%3d -- active=%3d -- cf:%7.1f Hz =%6.2f ERBs -- x=%.3fcm delta=%.3fcm b=%.3fcm A=%.3fcm2\n",
248 state->total_chans - chan, counter * segmentPointer->active, freq, ErbScale( freq ), xn, delta_xn, bn, an ) ;
249 }
250
251 /*** scale output ***/
252 scale_vel = output_gain * FILTERBANK_SCALE ;
253 scale_disp = TwoPi * REF_CF * scale_vel ;
254
255 /*** get filter states ***/
256 state->states[ chan ] = WDFilter( samplerate, freq, scale_vel, scale_disp, RHO_fluid, an, bn,
257 qfactor, MASS_PER_AREA, delta_xn, Lt, WARPING,
258 segmentPointer->active, &zov, OHC_gain, OHC_sat ) ;
259
260 Delete( segmentPointer ) ;
261 }
262
263 Delete( bankSeg ) ;
264
265
266 /*** set parameters common to all channels ***/
267
268 state->bank = New( TLF_BankInfo * ) ;
269 state->bank->output_chans = state->display_chans ;
270
271 /* set decimation parameters */
272 state->bank->decimateCount = 0 ;
273 *decimate_factor = MAX( 1, *decimate_factor ) ;
274 state->bank->decimate_factor = *decimate_factor ;
275
276
277 /***** set output and nonlinearity variables, and filter procedure *****/
278
279 if( strncmp( motionstr, "velocity", 3 ) == 0 ) {
280 state->proc = DoWDFdataArray ;
281 state->bank->output_var = VELOCITY ;
282 state->bank->nl_var = DISPLACEMENT ;
283 }
284 else {
285 state->proc = DoWDFdataArray ;
286 state->bank->output_var = DISPLACEMENT ;
287 state->bank->nl_var = DISPLACEMENT ;
288 }
289
290
291 /***** Set WDF-EAR filter design parameters *****/
292 state->wave_states = FreefieldWDF( samplerate, RHO_air, C, As, concha->diameter/2. ) ;
293
294 state->Ntube = concha->Nsegments + canal->Nsegments ;
295 state->eartube_states = NewArray( EartubeWDFstate *, state->Ntube, "ear.c for eartube states" ) ;
296
297 length = concha->length / concha->Nsegments ;
298 diam = concha->diameter ;
299 attn = concha->att_factor ;
300 for( segment = 0 ; segment < concha->Nsegments ; segment++ )
301 state->eartube_states[ segment ] = EartubeWDF( samplerate, RHO_air, C, diam, length, attn ) ;
302
303 length = canal->length / canal->Nsegments ;
304 diam = canal->diameter ;
305 attn = canal->att_factor ;
306 for( ; segment < state->Ntube ; segment++ )
307 state->eartube_states[ segment ] = EartubeWDF( samplerate, RHO_air, C, diam, length, attn ) ;
308
309 state->earmiddle_states = EarmiddleWDF( samplerate, zov, 1.0, RATIO ) ;
310
311
312 /***** specify procedures upon closing *****/
313 state->closeEar = CloseEarWDF ;
314 state->closeTLF = CloseWDF ;
315
316 /***** initialise output-related parameters and return *****/
317 outputSource = SetFillableSource( state, tlf_bank_callback, "bank_tl.c filter" ) ;
318
319 for( *chans = state->display_chans ; interps-- > 0 ; *chans = *chans * 2 - 1, density = density * 2. )
320 outputSource = InterpSource( outputSource, *chans ) ;
321
322 return ( outputSource ) ;
323 }
324
325
326 /************************** lower level functions *************************/
327
328 BankSegment **GenerateBankSegments( interps, samplerate, display_frequencies, display_dens,
329 out_dens, display_chans, total_chans )
330 int interps ;
331 double samplerate ;
332 double *display_frequencies ;
333 double *display_dens, *out_dens ;
334 int *display_chans, *total_chans ;
335 {
336 BankSegment **bankSeg, *segmentPointer ;
337 double min_cf, max_cf ;
338 double apical_cf, basal_cf ;
339 double *apical_frequencies, *basal_frequencies ;
340 double apical_dens, basal_dens, central_dens ;
341 int apical_chans, basal_chans, central_chans ;
342 int ichan, chan ;
343 int remainder ;
344 double cm_per_CB = GetERBscaling ( ) / 10. ;
345
346
347 /***** set number and density of central channels *****/
348 if( interps >= 0 ) {
349
350 central_chans = *display_chans = *display_chans >> interps ;
351 central_dens = *display_dens = ldexp( *display_dens, -interps ) ;
352 min_cf = FofErbScale( ErbScale( display_frequencies[0] ) - 1. / central_dens ) ;
353 max_cf = display_frequencies[ ( central_chans - 1 ) << interps ] ;
354 }
355
356 else {
357 central_chans = *display_chans << -interps ;
358 central_dens = ldexp( *display_dens, -interps ) ;
359 min_cf = FofErbScale( ErbScale( display_frequencies[0] )
360 - ( 1 << -interps ) / central_dens ) ;
361 max_cf = display_frequencies[*display_chans - 1] ;
362 }
363
364
365 /***** set number and density of apical and basal channels outside display range *****/
366
367 if( *out_dens <= 0. ) {
368
369 apical_cf = min_cf ;
370 apical_dens = 0. ;
371 basal_cf = max_cf ;
372 basal_dens = 0. ;
373 }
374 else {
375 apical_cf = adjustCF( MIN_CF, samplerate ) ;
376 apical_dens = *out_dens ;
377 basal_cf = adjustCF( MAX_CF, samplerate ) ;
378 basal_dens = *out_dens ;
379 }
380
381 apical_chans = 0 ;
382 basal_chans = 0 ;
383
384
385 /***** generate apical and basal center frequencies outside display range *****/
386
387 apical_frequencies = GenerateFrequencies( apical_cf, min_cf, min_cf, &apical_dens, &apical_chans ) ;
388 basal_frequencies = GenerateFrequencies( max_cf, basal_cf, max_cf, &basal_dens, &basal_chans ) ;
389
390 /***** fill in array of BanKSegment structures with BM segment info *****/
391 *total_chans = apical_chans + central_chans + basal_chans - 2 ;
392 bankSeg = NewArray( BankSegment *, *total_chans + 1, "tl_bank.c for segments" ) ;
393
394 ichan = 0 ;
395 for( chan = 1 ; chan < apical_chans ; chan++ ) {
396
397 bankSeg[ ichan++ ] = segmentPointer = New( BankSegment * ) ;
398 segmentPointer->center_frequency = apical_frequencies[ chan ] ;
399 segmentPointer->seglength = cm_per_CB / apical_dens ;
400 segmentPointer->position = L - cm_per_CB * ErbScale( segmentPointer->center_frequency ) ;
401 segmentPointer->active = 0 ;
402 }
403
404 for( chan = 0 ; chan < central_chans ; chan++ ) {
405
406 bankSeg[ ichan++ ] = segmentPointer = New( BankSegment * ) ;
407
408 if( interps >= 0 ) {
409 segmentPointer->center_frequency = display_frequencies[ chan << interps ] ;
410 segmentPointer->active = 1 ;
411 }
412 else {
413 remainder = chan % ( 1 << -interps ) ;
414 segmentPointer->center_frequency = FofErbScale( ErbScale( display_frequencies[ chan >> -interps ] )
415 + ( remainder - ( 1 << -interps ) + 1 ) / central_dens ) ;
416 segmentPointer->active = ( remainder == ( ( 1 << -interps ) - 1 ) ) ;
417 }
418
419 segmentPointer->seglength = cm_per_CB / central_dens ;
420 segmentPointer->position = L - cm_per_CB * ErbScale( segmentPointer->center_frequency ) ;
421 }
422
423
424 for( chan = 1 ; chan < basal_chans ; chan++ ) {
425
426 bankSeg[ ichan++ ] = segmentPointer = New( BankSegment * ) ;
427 segmentPointer->center_frequency = basal_frequencies[ chan ] ;
428 segmentPointer->seglength = cm_per_CB / basal_dens ;
429 segmentPointer->position = L - cm_per_CB * ErbScale( segmentPointer->center_frequency ) ;
430 segmentPointer->active = 0 ;
431 }
432
433 segmentPointer = bankSeg[ 0 ] ;
434 if( ( segmentPointer->position + segmentPointer->seglength ) > L )
435 segmentPointer->seglength = L - MIN( L, segmentPointer->position ) ;
436
437 /***** return and deallocate dynamic memory *****/
438 Delete( apical_frequencies ) ;
439 Delete( basal_frequencies ) ;
440
441 return( bankSeg ) ;
442 }
443
444
445 double *GenerateFrequencies( min_cf, max_cf, base_cf, density, channels )
446 double min_cf, max_cf, base_cf ;
447 double *density ;
448 int *channels ;
449 {
450 double freq, *frequencies ;
451
452 /*** map characteristic frequencies onto specified scale ***/
453 min_cf = ErbScale( min_cf ) ;
454 max_cf = ErbScale( max_cf ) ;
455 max_cf = MAX( min_cf, max_cf ) ; /* max_cf cannot be smaller than min_cf */
456 base_cf = ErbScale( base_cf ) ;
457
458
459 /*** call appropriate generating functions ***/
460 if( *channels <= 0 ) {
461
462 if( *density <= 0. ) {
463 *channels = 1 ; /* there must be at least one channel */
464 freq = FofErbScale( min_cf ) ; /* arbitrary */
465 frequencies = &freq ;
466 *density = 1. ; /* arbitrary */
467
468 }
469 else {
470 frequencies = GenerateScale( min_cf, max_cf, *density, base_cf, FofErbScale ) ;
471 *channels = NumberOnScale( min_cf, max_cf, *density, base_cf ) ;
472 }
473 }
474
475 else {
476 frequencies = NumberedScale( min_cf, max_cf, *channels, FofErbScale ) ;
477 *density = DensityOnScale( min_cf, max_cf, *channels ) ;
478 if( *density == 0. )
479 *density = 1. ; /* arbitrary */
480 }
481
482 /*** return pointer to array of center frequencies ***/
483 return ( frequencies ) ;
484 }
485
486
487 double adjustCF( cf, samplerate )
488 double cf, samplerate ;
489 {
490 cf = MAX( cf, MIN_CF ) ;
491 cf = MAX( cf, FofErbScale( 0. ) ) ;
492
493 cf = MIN( cf, samplerate / 2. ) ;
494 cf = MIN( cf, MAX_CF ) ;
495 cf = MIN( cf, FofErbScale( 10. * L / GetERBscaling() ) );
496
497 return ( cf ) ;
498 }
499