dicklyon@527
|
1 % Copyright 2012, Google, Inc.
|
dicklyon@527
|
2 % Author: Richard F. Lyon
|
dicklyon@527
|
3 %
|
dicklyon@527
|
4 % This Matlab file is part of an implementation of Lyon's cochlear model:
|
dicklyon@527
|
5 % "Cascade of Asymmetric Resonators with Fast-Acting Compression"
|
dicklyon@527
|
6 % to supplement Lyon's upcoming book "Human and Machine Hearing"
|
dicklyon@527
|
7 %
|
dicklyon@527
|
8 % Licensed under the Apache License, Version 2.0 (the "License");
|
dicklyon@527
|
9 % you may not use this file except in compliance with the License.
|
dicklyon@527
|
10 % You may obtain a copy of the License at
|
dicklyon@527
|
11 %
|
dicklyon@527
|
12 % http://www.apache.org/licenses/LICENSE-2.0
|
dicklyon@527
|
13 %
|
dicklyon@527
|
14 % Unless required by applicable law or agreed to in writing, software
|
dicklyon@527
|
15 % distributed under the License is distributed on an "AS IS" BASIS,
|
dicklyon@527
|
16 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
dicklyon@527
|
17 % See the License for the specific language governing permissions and
|
dicklyon@527
|
18 % limitations under the License.
|
dicklyon@527
|
19
|
dicklyon@526
|
20 function [complex_transfns_freqs, ...
|
dicklyon@533
|
21 stage_numerators, stage_denominators, group_delays] = ...
|
dicklyon@533
|
22 CARFAC_Transfer_Functions(CF, freqs, to_channels, from_channels)
|
dicklyon@526
|
23 % function [complex_transfns_freqs, ...
|
dicklyon@533
|
24 % stage_numerators, stage_denominators, group_delays] = ...
|
dicklyon@533
|
25 % CARFAC_Transfer_Functions(CF, freqs, to_channels, from_channels)
|
dicklyon@533
|
26 %
|
dicklyon@526
|
27 % Return transfer functions as polynomials in z (nums & denoms);
|
dicklyon@526
|
28 % And evaluate them at freqs if it's given, to selected output,
|
dicklyon@526
|
29 % optionally from selected starting points (from 0, input, by default).
|
dicklyon@526
|
30 % complex_transfns_freqs has a row of complex gains per to_channel.
|
dicklyon@526
|
31
|
dicklyon@526
|
32 % always start with the rational functions, whether we want to return
|
dicklyon@561
|
33 % them or not; this defaults to ear 1 only:
|
dicklyon@526
|
34 [stage_numerators, stage_denominators] = CARFAC_Rational_Functions(CF);
|
dicklyon@526
|
35
|
dicklyon@526
|
36 if nargin >= 2
|
dicklyon@526
|
37 % Evaluate at the provided list of frequencies.
|
dicklyon@526
|
38 if ~isrow(freqs)
|
dicklyon@526
|
39 if iscolumn(freqs)
|
dicklyon@526
|
40 freqs = freqs';
|
dicklyon@526
|
41 else
|
dicklyon@526
|
42 error('Bad freqs_row in CARFAC_Transfer_Functions');
|
dicklyon@526
|
43 end
|
dicklyon@526
|
44 end
|
dicklyon@526
|
45 if any(freqs < 0)
|
dicklyon@526
|
46 error('Negatives in freqs_row in CARFAC_Transfer_Functions');
|
dicklyon@526
|
47 end
|
dicklyon@526
|
48 z_row = exp((i * 2 * pi / CF.fs) * freqs); % z = exp(sT)
|
dicklyon@526
|
49 gains = Rational_Eval(stage_numerators, stage_denominators, z_row);
|
dicklyon@526
|
50
|
dicklyon@526
|
51 % Now multiply gains from input to output places; use logs?
|
dicklyon@526
|
52 log_gains = log(gains);
|
dicklyon@565
|
53 cum_log_gains = cumsum(log_gains); % accum across cascaded stages
|
dicklyon@526
|
54
|
dicklyon@526
|
55 % And figure out which cascade products we want:
|
dicklyon@526
|
56 n_ch = CF.n_ch;
|
dicklyon@526
|
57 if nargin < 3
|
dicklyon@526
|
58 to_channels = 1:n_ch;
|
dicklyon@526
|
59 end
|
dicklyon@526
|
60 if isempty(to_channels) || any(to_channels < 1 | to_channels > n_ch)
|
dicklyon@526
|
61 error('Bad to_channels in CARFAC_Transfer_Functions');
|
dicklyon@526
|
62 end
|
dicklyon@526
|
63 if nargin < 4 || isempty(from_channels)
|
dicklyon@526
|
64 from_channels = 0; % tranfuns from input, called channel 0.
|
dicklyon@526
|
65 end
|
dicklyon@526
|
66 if length(from_channels) == 1
|
dicklyon@528
|
67 from_channels = from_channels * ones(1,length(to_channels));
|
dicklyon@526
|
68 end
|
dicklyon@526
|
69 % Default to cum gain of 1 (log is 0), from input channel 0:
|
dicklyon@526
|
70 from_cum = zeros(length(to_channels), length(z_row));
|
dicklyon@526
|
71 not_input = from_channels > 0;
|
dicklyon@526
|
72 from_cum(not_input, :) = cum_log_gains(from_channels(not_input), :);
|
dicklyon@526
|
73 log_transfns = cum_log_gains(to_channels, :) - from_cum;
|
dicklyon@526
|
74 complex_transfns_freqs = exp(log_transfns);
|
dicklyon@533
|
75
|
dicklyon@533
|
76 if nargout >= 4
|
dicklyon@533
|
77 phases = imag(log_gains); % no wrapping problem on single stages
|
dicklyon@533
|
78 cum_phases = cumsum(phases); % so no wrapping here either
|
dicklyon@533
|
79 group_delays = -diff(cum_phases')'; % diff across frequencies
|
dicklyon@533
|
80 group_delays = group_delays ./ (2*pi*repmat(diff(freqs), n_ch, 1));
|
dicklyon@533
|
81 end
|
dicklyon@526
|
82 else
|
dicklyon@526
|
83 % If no freqs are provided, do nothing but return the stage info above:
|
dicklyon@526
|
84 complex_transfns_freqs = [];
|
dicklyon@526
|
85 end
|
dicklyon@526
|
86
|
dicklyon@526
|
87
|
dicklyon@526
|
88
|
dicklyon@526
|
89 function gains = Rational_Eval(numerators, denominators, z_row)
|
dicklyon@526
|
90 % function gains = Rational_Eval(numerators, denominators, z_row)
|
dicklyon@526
|
91 % Evaluate rational function at row of z values.
|
dicklyon@526
|
92
|
dicklyon@526
|
93 zz = [z_row .* z_row; z_row; ones(size(z_row))];
|
dicklyon@526
|
94 % dot product of each poly row with each [z2; z; 1] col:
|
dicklyon@526
|
95 gains = (numerators * zz) ./ (denominators * zz);
|
dicklyon@526
|
96
|
dicklyon@526
|
97
|
dicklyon@565
|
98
|
dicklyon@526
|
99 function [stage_numerators, stage_denominators] = ...
|
dicklyon@561
|
100 CARFAC_Rational_Functions(CF, ear)
|
dicklyon@526
|
101 % function [stage_z_numerators, stage_z_denominators] = ...
|
dicklyon@561
|
102 % CARFAC_Rational_Functions(CF, ear)
|
dicklyon@526
|
103 % Return transfer functions of all stages as rational functions.
|
dicklyon@526
|
104
|
dicklyon@561
|
105 if nargin < 2
|
dicklyon@561
|
106 ear = 1;
|
dicklyon@561
|
107 end
|
dicklyon@561
|
108
|
dicklyon@528
|
109 n_ch = CF.n_ch;
|
dicklyon@561
|
110 coeffs = CF.ears(ear).CAR_coeffs;
|
dicklyon@528
|
111
|
dicklyon@530
|
112 a0 = coeffs.a0_coeffs;
|
dicklyon@530
|
113 c0 = coeffs.c0_coeffs;
|
dicklyon@530
|
114 zr = coeffs.zr_coeffs;
|
dicklyon@528
|
115
|
dicklyon@528
|
116 % get r, adapted if we have state:
|
dicklyon@565
|
117 r1 = coeffs.r1_coeffs; % max-damping condition
|
dicklyon@561
|
118 if isfield(CF.ears(ear), 'CAR_state')
|
dicklyon@561
|
119 state = CF.ears(ear).CAR_state;
|
dicklyon@565
|
120 zB = state.zB_memory; % current delta-r from undamping
|
dicklyon@565
|
121 r = r1 + zB;
|
dicklyon@526
|
122 else
|
dicklyon@565
|
123 zB = 0; % HIGH-level linear condition by default
|
dicklyon@526
|
124 end
|
dicklyon@528
|
125
|
dicklyon@565
|
126 relative_undamping = zB ./ zr;
|
dicklyon@565
|
127 g = CARFAC_Stage_g(coeffs, relative_undamping);
|
dicklyon@528
|
128 a = a0 .* r;
|
dicklyon@528
|
129 c = c0 .* r;
|
dicklyon@526
|
130 r2 = r .* r;
|
dicklyon@528
|
131 h = coeffs.h_coeffs;
|
dicklyon@528
|
132
|
dicklyon@526
|
133 stage_denominators = [ones(n_ch, 1), -2 * a, r2];
|
dicklyon@526
|
134 stage_numerators = [g .* ones(n_ch, 1), g .* (-2 * a + h .* c), g .* r2];
|
dicklyon@526
|
135
|
dicklyon@526
|
136
|
dicklyon@526
|
137 %% example
|
dicklyon@526
|
138 % CF = CARFAC_Design
|
dicklyon@526
|
139 % f = (0:100).^2; % frequencies to 10 kHz, unequally spaced
|
dicklyon@526
|
140 % to_ch = 10:10:96; % selected output channels
|
dicklyon@526
|
141 % from_ch = to_ch - 10; % test the inclusion of 0 in from list
|
dicklyon@526
|
142 % tf = CARFAC_Transfer_Functions(CF, f, to_ch, from_ch);
|
dicklyon@526
|
143 % figure
|
dicklyon@526
|
144 % plot(f, 20*log(abs(tf)')/log(10)); % dB vs lin. freq for 10 taps
|
dicklyon@526
|
145
|