alexbrandmeyer@609
|
1 //
|
alexbrandmeyer@609
|
2 // ear.cc
|
alexbrandmeyer@609
|
3 // CARFAC Open Source C++ Library
|
alexbrandmeyer@609
|
4 //
|
alexbrandmeyer@609
|
5 // Created by Alex Brandmeyer on 5/10/13.
|
alexbrandmeyer@609
|
6 //
|
alexbrandmeyer@609
|
7 // This C++ file is part of an implementation of Lyon's cochlear model:
|
alexbrandmeyer@609
|
8 // "Cascade of Asymmetric Resonators with Fast-Acting Compression"
|
alexbrandmeyer@609
|
9 // to supplement Lyon's upcoming book "Human and Machine Hearing"
|
alexbrandmeyer@609
|
10 //
|
alexbrandmeyer@609
|
11 // Licensed under the Apache License, Version 2.0 (the "License");
|
alexbrandmeyer@609
|
12 // you may not use this file except in compliance with the License.
|
alexbrandmeyer@609
|
13 // You may obtain a copy of the License at
|
alexbrandmeyer@609
|
14 //
|
alexbrandmeyer@609
|
15 // http://www.apache.org/licenses/LICENSE-2.0
|
alexbrandmeyer@609
|
16 //
|
alexbrandmeyer@609
|
17 // Unless required by applicable law or agreed to in writing, software
|
alexbrandmeyer@609
|
18 // distributed under the License is distributed on an "AS IS" BASIS,
|
alexbrandmeyer@609
|
19 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
alexbrandmeyer@609
|
20 // See the License for the specific language governing permissions and
|
alexbrandmeyer@609
|
21 // limitations under the License.
|
alexbrandmeyer@609
|
22
|
ronw@628
|
23 #include <assert.h>
|
alexbrandmeyer@609
|
24 #include "ear.h"
|
alexbrandmeyer@609
|
25
|
ronw@645
|
26 Ear::Ear(const int num_channels, const CARCoeffs& car_coeffs,
|
ronw@645
|
27 const IHCCoeffs& ihc_coeffs,
|
ronw@645
|
28 const std::vector<AGCCoeffs>& agc_coeffs) {
|
ronw@645
|
29 Reset(num_channels, car_coeffs, ihc_coeffs, agc_coeffs);
|
ronw@645
|
30 }
|
ronw@645
|
31
|
ronw@645
|
32 void Ear::Reset(const int num_channels, const CARCoeffs& car_coeffs,
|
ronw@645
|
33 const IHCCoeffs& ihc_coeffs,
|
ronw@645
|
34 const std::vector<AGCCoeffs>& agc_coeffs) {
|
alexbrandmeyer@643
|
35 num_channels_ = num_channels;
|
alexbrandmeyer@636
|
36 car_coeffs_ = car_coeffs;
|
alexbrandmeyer@636
|
37 ihc_coeffs_ = ihc_coeffs;
|
alexbrandmeyer@636
|
38 agc_coeffs_ = agc_coeffs;
|
ronw@645
|
39 ResetCARState();
|
ronw@645
|
40 ResetIHCState();
|
ronw@645
|
41 ResetAGCState();
|
alexbrandmeyer@609
|
42 }
|
alexbrandmeyer@609
|
43
|
ronw@645
|
44 void Ear::ResetCARState() {
|
alexbrandmeyer@643
|
45 car_state_.z1_memory.setZero(num_channels_);
|
alexbrandmeyer@643
|
46 car_state_.z2_memory.setZero(num_channels_);
|
alexbrandmeyer@643
|
47 car_state_.za_memory.setZero(num_channels_);
|
alexbrandmeyer@640
|
48 car_state_.zb_memory = car_coeffs_.zr_coeffs;
|
alexbrandmeyer@643
|
49 car_state_.dzb_memory.setZero(num_channels_);
|
alexbrandmeyer@643
|
50 car_state_.zy_memory.setZero(num_channels_);
|
alexbrandmeyer@640
|
51 car_state_.g_memory = car_coeffs_.g0_coeffs;
|
alexbrandmeyer@643
|
52 car_state_.dg_memory.setZero(num_channels_);
|
alexbrandmeyer@610
|
53 }
|
alexbrandmeyer@610
|
54
|
ronw@645
|
55 void Ear::ResetIHCState() {
|
alexbrandmeyer@643
|
56 ihc_state_.ihc_accum = ArrayX::Zero(num_channels_);
|
alexbrandmeyer@640
|
57 if (! ihc_coeffs_.just_half_wave_rectify) {
|
alexbrandmeyer@643
|
58 ihc_state_.ac_coupler.setZero(num_channels_);
|
alexbrandmeyer@643
|
59 ihc_state_.lpf1_state.setConstant(num_channels_, ihc_coeffs_.rest_output);
|
alexbrandmeyer@643
|
60 ihc_state_.lpf2_state.setConstant(num_channels_, ihc_coeffs_.rest_output);
|
alexbrandmeyer@640
|
61 if (ihc_coeffs_.one_capacitor) {
|
alexbrandmeyer@643
|
62 ihc_state_.cap1_voltage.setConstant(num_channels_, ihc_coeffs_.rest_cap1);
|
alexbrandmeyer@610
|
63 } else {
|
alexbrandmeyer@643
|
64 ihc_state_.cap1_voltage.setConstant(num_channels_, ihc_coeffs_.rest_cap1);
|
alexbrandmeyer@643
|
65 ihc_state_.cap2_voltage.setConstant(num_channels_, ihc_coeffs_.rest_cap2);
|
alexbrandmeyer@610
|
66 }
|
alexbrandmeyer@610
|
67 }
|
alexbrandmeyer@610
|
68 }
|
alexbrandmeyer@610
|
69
|
ronw@645
|
70 void Ear::ResetAGCState() {
|
alexbrandmeyer@626
|
71 int n_agc_stages = agc_coeffs_.size();
|
alexbrandmeyer@626
|
72 agc_state_.resize(n_agc_stages);
|
alexbrandmeyer@636
|
73 for (auto& stage_state : agc_state_) {
|
alexbrandmeyer@640
|
74 stage_state.decim_phase = 0;
|
alexbrandmeyer@643
|
75 stage_state.agc_memory.setZero(num_channels_);
|
alexbrandmeyer@643
|
76 stage_state.input_accum.setZero(num_channels_);
|
alexbrandmeyer@610
|
77 }
|
alexbrandmeyer@610
|
78 }
|
alexbrandmeyer@610
|
79
|
alexbrandmeyer@637
|
80 void Ear::CARStep(const FPType input) {
|
alexbrandmeyer@626
|
81 // This interpolates g.
|
alexbrandmeyer@640
|
82 car_state_.g_memory = car_state_.g_memory + car_state_.dg_memory;
|
alexbrandmeyer@610
|
83 // This calculates the AGC interpolation state.
|
alexbrandmeyer@640
|
84 car_state_.zb_memory = car_state_.zb_memory + car_state_.dzb_memory;
|
alexbrandmeyer@610
|
85 // This updates the nonlinear function of 'velocity' along with zA, which is
|
alexbrandmeyer@610
|
86 // a delay of z2.
|
alexbrandmeyer@643
|
87 ArrayX nonlinear_fun(num_channels_);
|
alexbrandmeyer@643
|
88 ArrayX velocities = car_state_.z2_memory - car_state_.za_memory;
|
alexbrandmeyer@626
|
89 OHCNonlinearFunction(velocities, &nonlinear_fun);
|
alexbrandmeyer@626
|
90 // Here, zb_memory_ * nonlinear_fun is "undamping" delta r.
|
alexbrandmeyer@643
|
91 ArrayX r = car_coeffs_.r1_coeffs + (car_state_.zb_memory * nonlinear_fun);
|
alexbrandmeyer@640
|
92 car_state_.za_memory = car_state_.z2_memory;
|
alexbrandmeyer@610
|
93 // Here we reduce the CAR state by r and rotate with the fixed cos/sin coeffs.
|
alexbrandmeyer@643
|
94 ArrayX z1 = r * ((car_coeffs_.a0_coeffs * car_state_.z1_memory) -
|
alexbrandmeyer@643
|
95 (car_coeffs_.c0_coeffs * car_state_.z2_memory));
|
alexbrandmeyer@640
|
96 car_state_.z2_memory = r *
|
alexbrandmeyer@640
|
97 ((car_coeffs_.c0_coeffs * car_state_.z1_memory) +
|
alexbrandmeyer@640
|
98 (car_coeffs_.a0_coeffs * car_state_.z2_memory));
|
alexbrandmeyer@640
|
99 car_state_.zy_memory = car_coeffs_.h_coeffs * car_state_.z2_memory;
|
alexbrandmeyer@610
|
100 // This section ripples the input-output path, to avoid delay...
|
alexbrandmeyer@610
|
101 // It's the only part that doesn't get computed "in parallel":
|
alexbrandmeyer@626
|
102 FPType in_out = input;
|
alexbrandmeyer@643
|
103 for (int channel = 0; channel < num_channels_; channel++) {
|
alexbrandmeyer@643
|
104 z1(channel) = z1(channel) + in_out;
|
alexbrandmeyer@610
|
105 // This performs the ripple, and saves the final channel outputs in zy.
|
alexbrandmeyer@643
|
106 in_out = car_state_.g_memory(channel) *
|
alexbrandmeyer@643
|
107 (in_out + car_state_.zy_memory(channel));
|
alexbrandmeyer@643
|
108 car_state_.zy_memory(channel) = in_out;
|
alexbrandmeyer@609
|
109 }
|
alexbrandmeyer@640
|
110 car_state_.z1_memory = z1;
|
alexbrandmeyer@609
|
111 }
|
alexbrandmeyer@609
|
112
|
alexbrandmeyer@610
|
113 // We start with a quadratic nonlinear function, and limit it via a
|
alexbrandmeyer@610
|
114 // rational function. This makes the result go to zero at high
|
alexbrandmeyer@609
|
115 // absolute velocities, so it will do nothing there.
|
alexbrandmeyer@643
|
116 void Ear::OHCNonlinearFunction(const ArrayX& velocities,
|
alexbrandmeyer@643
|
117 ArrayX* nonlinear_fun) {
|
alexbrandmeyer@640
|
118 *nonlinear_fun = (1 + ((velocities * car_coeffs_.velocity_scale) +
|
alexbrandmeyer@640
|
119 car_coeffs_.v_offset).square()).inverse();
|
alexbrandmeyer@609
|
120 }
|
alexbrandmeyer@609
|
121
|
alexbrandmeyer@610
|
122 // This step is a one sample-time update of the inner-hair-cell (IHC) model,
|
alexbrandmeyer@610
|
123 // including the detection nonlinearity and either one or two capacitor state
|
alexbrandmeyer@610
|
124 // variables.
|
alexbrandmeyer@643
|
125 void Ear::IHCStep(const ArrayX& car_out) {
|
alexbrandmeyer@643
|
126 ArrayX ac_diff = car_out - ihc_state_.ac_coupler;
|
alexbrandmeyer@640
|
127 ihc_state_.ac_coupler = ihc_state_.ac_coupler +
|
alexbrandmeyer@640
|
128 (ihc_coeffs_.ac_coeff * ac_diff);
|
alexbrandmeyer@640
|
129 if (ihc_coeffs_.just_half_wave_rectify) {
|
alexbrandmeyer@643
|
130 ArrayX output(num_channels_);
|
alexbrandmeyer@643
|
131 for (int channel = 0; channel < num_channels_; ++channel) {
|
alexbrandmeyer@643
|
132 FPType a = (ac_diff(channel) > 0.0) ? ac_diff(channel) : 0.0;
|
alexbrandmeyer@643
|
133 output(channel) = (a < 2) ? a : 2;
|
alexbrandmeyer@609
|
134 }
|
alexbrandmeyer@640
|
135 ihc_state_.ihc_out = output;
|
alexbrandmeyer@609
|
136 } else {
|
alexbrandmeyer@643
|
137 ArrayX conductance = CARFACDetect(ac_diff);
|
alexbrandmeyer@640
|
138 if (ihc_coeffs_.one_capacitor) {
|
alexbrandmeyer@640
|
139 ihc_state_.ihc_out = conductance * ihc_state_.cap1_voltage;
|
alexbrandmeyer@640
|
140 ihc_state_.cap1_voltage = ihc_state_.cap1_voltage -
|
alexbrandmeyer@640
|
141 (ihc_state_.ihc_out * ihc_coeffs_.out1_rate) +
|
alexbrandmeyer@640
|
142 ((1 - ihc_state_.cap1_voltage) * ihc_coeffs_.in1_rate);
|
alexbrandmeyer@609
|
143 } else {
|
alexbrandmeyer@640
|
144 ihc_state_.ihc_out = conductance * ihc_state_.cap2_voltage;
|
alexbrandmeyer@640
|
145 ihc_state_.cap1_voltage = ihc_state_.cap1_voltage -
|
alexbrandmeyer@640
|
146 ((ihc_state_.cap1_voltage - ihc_state_.cap2_voltage)
|
alexbrandmeyer@640
|
147 * ihc_coeffs_.out1_rate) + ((1 - ihc_state_.cap1_voltage) *
|
alexbrandmeyer@640
|
148 ihc_coeffs_.in1_rate);
|
alexbrandmeyer@640
|
149 ihc_state_.cap2_voltage = ihc_state_.cap2_voltage -
|
alexbrandmeyer@640
|
150 (ihc_state_.ihc_out * ihc_coeffs_.out2_rate) +
|
alexbrandmeyer@640
|
151 ((ihc_state_.cap1_voltage - ihc_state_.cap2_voltage)
|
alexbrandmeyer@640
|
152 * ihc_coeffs_.in2_rate);
|
alexbrandmeyer@609
|
153 }
|
alexbrandmeyer@610
|
154 // Here we smooth the output twice using a LPF.
|
alexbrandmeyer@640
|
155 ihc_state_.ihc_out *= ihc_coeffs_.output_gain;
|
alexbrandmeyer@640
|
156 ihc_state_.lpf1_state += ihc_coeffs_.lpf_coeff *
|
alexbrandmeyer@640
|
157 (ihc_state_.ihc_out - ihc_state_.lpf1_state);
|
alexbrandmeyer@640
|
158 ihc_state_.lpf2_state += ihc_coeffs_.lpf_coeff *
|
alexbrandmeyer@640
|
159 (ihc_state_.lpf1_state - ihc_state_.lpf2_state);
|
alexbrandmeyer@640
|
160 ihc_state_.ihc_out = ihc_state_.lpf2_state - ihc_coeffs_.rest_output;
|
alexbrandmeyer@609
|
161 }
|
alexbrandmeyer@640
|
162 ihc_state_.ihc_accum += ihc_state_.ihc_out;
|
alexbrandmeyer@609
|
163 }
|
alexbrandmeyer@609
|
164
|
alexbrandmeyer@643
|
165 bool Ear::AGCStep(const ArrayX& ihc_out) {
|
alexbrandmeyer@609
|
166 int stage = 0;
|
alexbrandmeyer@643
|
167 int num_stages = agc_coeffs_[0].num_agc_stages;
|
alexbrandmeyer@643
|
168 FPType detect_scale = agc_coeffs_[num_stages - 1].detect_scale;
|
alexbrandmeyer@626
|
169 bool updated = AGCRecurse(stage, detect_scale * ihc_out);
|
alexbrandmeyer@609
|
170 return updated;
|
alexbrandmeyer@609
|
171 }
|
alexbrandmeyer@609
|
172
|
alexbrandmeyer@643
|
173 bool Ear::AGCRecurse(const int stage, ArrayX agc_in) {
|
alexbrandmeyer@609
|
174 bool updated = true;
|
alexbrandmeyer@636
|
175 const auto& agc_coeffs = agc_coeffs_[stage];
|
alexbrandmeyer@636
|
176 auto& agc_state = agc_state_[stage];
|
alexbrandmeyer@610
|
177 // This is the decim factor for this stage, relative to input or prev. stage:
|
alexbrandmeyer@640
|
178 int decim = agc_coeffs.decimation;
|
alexbrandmeyer@610
|
179 // This is the decim phase of this stage (do work on phase 0 only):
|
alexbrandmeyer@640
|
180 int decim_phase = agc_state.decim_phase + 1;
|
alexbrandmeyer@609
|
181 decim_phase = decim_phase % decim;
|
alexbrandmeyer@640
|
182 agc_state.decim_phase = decim_phase;
|
alexbrandmeyer@610
|
183 // Here we accumulate input for this stage from the previous stage:
|
alexbrandmeyer@640
|
184 agc_state.input_accum += agc_in;
|
alexbrandmeyer@610
|
185 // We don't do anything if it's not the right decim_phase.
|
alexbrandmeyer@610
|
186 if (decim_phase == 0) {
|
alexbrandmeyer@610
|
187 // Now we do lots of work, at the decimated rate.
|
alexbrandmeyer@610
|
188 // These are the decimated inputs for this stage, which will be further
|
alexbrandmeyer@610
|
189 // decimated at the next stage.
|
alexbrandmeyer@640
|
190 agc_in = agc_state.input_accum / decim;
|
alexbrandmeyer@610
|
191 // This resets the accumulator.
|
alexbrandmeyer@643
|
192 agc_state.input_accum = ArrayX::Zero(num_channels_);
|
alexbrandmeyer@626
|
193 if (stage < (agc_coeffs_.size() - 1)) {
|
alexbrandmeyer@610
|
194 // Now we recurse to evaluate the next stage(s).
|
alexbrandmeyer@610
|
195 updated = AGCRecurse(stage + 1, agc_in);
|
alexbrandmeyer@610
|
196 // Afterwards we add its output to this stage input, whether it updated or
|
alexbrandmeyer@610
|
197 // not.
|
alexbrandmeyer@640
|
198 agc_in += agc_coeffs.agc_stage_gain *
|
alexbrandmeyer@640
|
199 agc_state_[stage + 1].agc_memory;
|
alexbrandmeyer@609
|
200 }
|
alexbrandmeyer@610
|
201 // This performs a first-order recursive smoothing filter update, in time.
|
alexbrandmeyer@640
|
202 agc_state.agc_memory += agc_coeffs.agc_epsilon *
|
alexbrandmeyer@640
|
203 (agc_in - agc_state.agc_memory);
|
alexbrandmeyer@640
|
204 AGCSpatialSmooth(agc_coeffs_[stage], &agc_state.agc_memory);
|
alexbrandmeyer@626
|
205 updated = true;
|
alexbrandmeyer@609
|
206 } else {
|
alexbrandmeyer@609
|
207 updated = false;
|
alexbrandmeyer@609
|
208 }
|
alexbrandmeyer@609
|
209 return updated;
|
alexbrandmeyer@609
|
210 }
|
alexbrandmeyer@609
|
211
|
alexbrandmeyer@637
|
212 void Ear::AGCSpatialSmooth(const AGCCoeffs& agc_coeffs,
|
alexbrandmeyer@643
|
213 ArrayX* stage_state) {
|
alexbrandmeyer@643
|
214 int num_iterations = agc_coeffs.agc_spatial_iterations;
|
alexbrandmeyer@609
|
215 bool use_fir;
|
alexbrandmeyer@643
|
216 use_fir = (num_iterations < 4) ? true : false;
|
alexbrandmeyer@609
|
217 if (use_fir) {
|
alexbrandmeyer@640
|
218 FPType fir_coeffs_left = agc_coeffs.agc_spatial_fir_left;
|
alexbrandmeyer@640
|
219 FPType fir_coeffs_mid = agc_coeffs.agc_spatial_fir_mid;
|
alexbrandmeyer@640
|
220 FPType fir_coeffs_right = agc_coeffs.agc_spatial_fir_right;
|
alexbrandmeyer@643
|
221 ArrayX ss_tap1(num_channels_);
|
alexbrandmeyer@643
|
222 ArrayX ss_tap2(num_channels_);
|
alexbrandmeyer@643
|
223 ArrayX ss_tap3(num_channels_);
|
alexbrandmeyer@643
|
224 ArrayX ss_tap4(num_channels_);
|
alexbrandmeyer@640
|
225 int n_taps = agc_coeffs.agc_spatial_n_taps;
|
alexbrandmeyer@610
|
226 // This initializes the first two taps of stage state, which are used for
|
alexbrandmeyer@610
|
227 // both possible cases.
|
alexbrandmeyer@637
|
228 ss_tap1(0) = (*stage_state)(0);
|
alexbrandmeyer@643
|
229 ss_tap1.block(1, 0, num_channels_ - 1, 1) =
|
alexbrandmeyer@643
|
230 stage_state->block(0, 0, num_channels_ - 1, 1);
|
alexbrandmeyer@643
|
231 ss_tap2(num_channels_ - 1) = (*stage_state)(num_channels_ - 1);
|
alexbrandmeyer@643
|
232 ss_tap2.block(0, 0, num_channels_ - 1, 1) =
|
alexbrandmeyer@643
|
233 stage_state->block(1, 0, num_channels_ - 1, 1);
|
alexbrandmeyer@609
|
234 switch (n_taps) {
|
alexbrandmeyer@609
|
235 case 3:
|
alexbrandmeyer@637
|
236 *stage_state = (fir_coeffs_left * ss_tap1) +
|
alexbrandmeyer@637
|
237 (fir_coeffs_mid * *stage_state) + (fir_coeffs_right * ss_tap2);
|
alexbrandmeyer@609
|
238 break;
|
alexbrandmeyer@609
|
239 case 5:
|
alexbrandmeyer@610
|
240 // Now we initialize last two taps of stage state, which are only used
|
alexbrandmeyer@610
|
241 // for the 5-tap case.
|
alexbrandmeyer@637
|
242 ss_tap3(0) = (*stage_state)(0);
|
alexbrandmeyer@637
|
243 ss_tap3(1) = (*stage_state)(1);
|
alexbrandmeyer@643
|
244 ss_tap3.block(2, 0, num_channels_ - 2, 1) =
|
alexbrandmeyer@643
|
245 stage_state->block(0, 0, num_channels_ - 2, 1);
|
alexbrandmeyer@643
|
246 ss_tap4(num_channels_ - 2) = (*stage_state)(num_channels_ - 1);
|
alexbrandmeyer@643
|
247 ss_tap4(num_channels_ - 1) = (*stage_state)(num_channels_ - 2);
|
alexbrandmeyer@643
|
248 ss_tap4.block(0, 0, num_channels_ - 2, 1) =
|
alexbrandmeyer@643
|
249 stage_state->block(2, 0, num_channels_ - 2, 1);
|
alexbrandmeyer@637
|
250 *stage_state = (fir_coeffs_left * (ss_tap3 + ss_tap1)) +
|
alexbrandmeyer@637
|
251 (fir_coeffs_mid * *stage_state) +
|
alexbrandmeyer@637
|
252 (fir_coeffs_right * (ss_tap2 + ss_tap4));
|
alexbrandmeyer@609
|
253 break;
|
alexbrandmeyer@609
|
254 default:
|
ronw@628
|
255 assert(true && "Bad n_taps in AGCSpatialSmooth; should be 3 or 5.");
|
alexbrandmeyer@611
|
256 break;
|
alexbrandmeyer@609
|
257 }
|
alexbrandmeyer@609
|
258 } else {
|
alexbrandmeyer@640
|
259 AGCSmoothDoubleExponential(agc_coeffs.agc_pole_z1, agc_coeffs.agc_pole_z2,
|
alexbrandmeyer@640
|
260 stage_state);
|
alexbrandmeyer@609
|
261 }
|
alexbrandmeyer@609
|
262 }
|
alexbrandmeyer@609
|
263
|
alexbrandmeyer@643
|
264 void Ear::AGCSmoothDoubleExponential(const FPType pole_z1,
|
alexbrandmeyer@643
|
265 const FPType pole_z2,
|
alexbrandmeyer@643
|
266 ArrayX* stage_state) {
|
alexbrandmeyer@643
|
267 int32_t num_points = stage_state->size();
|
alexbrandmeyer@610
|
268 FPType input;
|
alexbrandmeyer@611
|
269 FPType state = 0.0;
|
alexbrandmeyer@626
|
270 // TODO (alexbrandmeyer): I'm assuming one dimensional input for now, but this
|
alexbrandmeyer@610
|
271 // should be verified with Dick for the final version
|
alexbrandmeyer@643
|
272 for (int i = num_points - 11; i < num_points; ++i) {
|
alexbrandmeyer@637
|
273 input = (*stage_state)(i);
|
alexbrandmeyer@610
|
274 state = state + (1 - pole_z1) * (input - state);
|
alexbrandmeyer@610
|
275 }
|
alexbrandmeyer@643
|
276 for (int i = num_points - 1; i > -1; --i) {
|
alexbrandmeyer@637
|
277 input = (*stage_state)(i);
|
alexbrandmeyer@610
|
278 state = state + (1 - pole_z2) * (input - state);
|
alexbrandmeyer@610
|
279 }
|
alexbrandmeyer@643
|
280 for (int i = 0; i < num_points; ++i) {
|
alexbrandmeyer@637
|
281 input = (*stage_state)(i);
|
alexbrandmeyer@610
|
282 state = state + (1 - pole_z1) * (input - state);
|
alexbrandmeyer@637
|
283 (*stage_state)(i) = state;
|
alexbrandmeyer@610
|
284 }
|
alexbrandmeyer@609
|
285 }
|
alexbrandmeyer@610
|
286
|
alexbrandmeyer@643
|
287 ArrayX Ear::StageGValue(const ArrayX& undamping) {
|
alexbrandmeyer@643
|
288 ArrayX r = car_coeffs_.r1_coeffs + car_coeffs_.zr_coeffs * undamping;
|
alexbrandmeyer@640
|
289 return (1 - 2 * r * car_coeffs_.a0_coeffs + (r * r)) /
|
alexbrandmeyer@640
|
290 (1 - 2 * r * car_coeffs_.a0_coeffs + car_coeffs_.h_coeffs * r *
|
alexbrandmeyer@640
|
291 car_coeffs_.c0_coeffs + (r * r));
|
ronw@645
|
292 }
|