Mercurial > hg > aim92
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 |