alexbrandmeyer@620
|
1 //
|
alexbrandmeyer@620
|
2 // ear.cc
|
alexbrandmeyer@620
|
3 // CARFAC Open Source C++ Library
|
alexbrandmeyer@620
|
4 //
|
alexbrandmeyer@620
|
5 // Created by Alex Brandmeyer on 5/10/13.
|
alexbrandmeyer@620
|
6 //
|
alexbrandmeyer@620
|
7 // This C++ file is part of an implementation of Lyon's cochlear model:
|
alexbrandmeyer@620
|
8 // "Cascade of Asymmetric Resonators with Fast-Acting Compression"
|
alexbrandmeyer@620
|
9 // to supplement Lyon's upcoming book "Human and Machine Hearing"
|
alexbrandmeyer@620
|
10 //
|
alexbrandmeyer@620
|
11 // Licensed under the Apache License, Version 2.0 (the "License");
|
alexbrandmeyer@620
|
12 // you may not use this file except in compliance with the License.
|
alexbrandmeyer@620
|
13 // You may obtain a copy of the License at
|
alexbrandmeyer@620
|
14 //
|
alexbrandmeyer@620
|
15 // http://www.apache.org/licenses/LICENSE-2.0
|
alexbrandmeyer@620
|
16 //
|
alexbrandmeyer@620
|
17 // Unless required by applicable law or agreed to in writing, software
|
alexbrandmeyer@620
|
18 // distributed under the License is distributed on an "AS IS" BASIS,
|
alexbrandmeyer@620
|
19 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
alexbrandmeyer@620
|
20 // See the License for the specific language governing permissions and
|
alexbrandmeyer@620
|
21 // limitations under the License.
|
alexbrandmeyer@620
|
22
|
alexbrandmeyer@620
|
23 #include "ear.h"
|
alexbrandmeyer@620
|
24
|
alexbrandmeyer@621
|
25 // The 'InitEar' function takes a set of model parameters and initializes the
|
alexbrandmeyer@621
|
26 // design coefficients and model state variables needed for running the model
|
alexbrandmeyer@668
|
27 // on a single audio channel.
|
alexbrandmeyer@668
|
28 void Ear::InitEar(const int n_ch, const FPType fs,
|
alexbrandmeyer@668
|
29 const FloatArray& pole_freqs, const CARParams& car_params,
|
alexbrandmeyer@668
|
30 const IHCParams& ihc_params, const AGCParams& agc_params) {
|
alexbrandmeyer@621
|
31 // The first section of code determines the number of channels that will be
|
alexbrandmeyer@621
|
32 // used in the model on the basis of the sample rate and the CAR parameters
|
alexbrandmeyer@621
|
33 // that have been passed to this function.
|
alexbrandmeyer@621
|
34 n_ch_ = n_ch;
|
alexbrandmeyer@621
|
35 // These functions use the parameters that have been passed to design the
|
alexbrandmeyer@668
|
36 // coefficients for the first two stages of the model.
|
alexbrandmeyer@668
|
37 car_coeffs_.Design(car_params, fs, pole_freqs);
|
alexbrandmeyer@668
|
38 ihc_coeffs_.Design(ihc_params, fs);
|
alexbrandmeyer@668
|
39 // This code initializes the coefficients for each of the AGC stages.
|
alexbrandmeyer@668
|
40 agc_coeffs_.resize(agc_params.n_stages_);
|
alexbrandmeyer@668
|
41 FPType previous_stage_gain = 0.0;
|
alexbrandmeyer@668
|
42 FPType decim = 1.0;
|
alexbrandmeyer@668
|
43 for (int stage = 0; stage < agc_params.n_stages_; ++stage) {
|
alexbrandmeyer@668
|
44 agc_coeffs_[stage].Design(agc_params, stage, fs, previous_stage_gain,
|
alexbrandmeyer@668
|
45 decim);
|
alexbrandmeyer@668
|
46 // Two variables store the decimation and gain levels for use in the design
|
alexbrandmeyer@668
|
47 // of the subsequent stages.
|
alexbrandmeyer@668
|
48 previous_stage_gain = agc_coeffs_[stage].agc_gain_;
|
alexbrandmeyer@668
|
49 decim = agc_coeffs_[stage].decim_;
|
alexbrandmeyer@668
|
50 }
|
alexbrandmeyer@621
|
51 // Once the coefficients have been determined, we can initialize the state
|
alexbrandmeyer@621
|
52 // variables that will be used during runtime.
|
alexbrandmeyer@621
|
53 InitCARState();
|
alexbrandmeyer@621
|
54 InitIHCState();
|
alexbrandmeyer@621
|
55 InitAGCState();
|
alexbrandmeyer@620
|
56 }
|
alexbrandmeyer@620
|
57
|
alexbrandmeyer@621
|
58 void Ear::InitCARState() {
|
alexbrandmeyer@621
|
59 car_state_.z1_memory_ = FloatArray::Zero(n_ch_);
|
alexbrandmeyer@621
|
60 car_state_.z2_memory_ = FloatArray::Zero(n_ch_);
|
alexbrandmeyer@621
|
61 car_state_.za_memory_ = FloatArray::Zero(n_ch_);
|
alexbrandmeyer@621
|
62 car_state_.zb_memory_ = car_coeffs_.zr_coeffs_;
|
alexbrandmeyer@621
|
63 car_state_.dzb_memory_ = FloatArray::Zero(n_ch_);
|
alexbrandmeyer@621
|
64 car_state_.zy_memory_ = FloatArray::Zero(n_ch_);
|
alexbrandmeyer@621
|
65 car_state_.g_memory_ = car_coeffs_.g0_coeffs_;
|
alexbrandmeyer@621
|
66 car_state_.dg_memory_ = FloatArray::Zero(n_ch_);
|
alexbrandmeyer@621
|
67 }
|
alexbrandmeyer@621
|
68
|
alexbrandmeyer@621
|
69 void Ear::InitIHCState() {
|
alexbrandmeyer@621
|
70 ihc_state_.ihc_accum_ = FloatArray::Zero(n_ch_);
|
alexbrandmeyer@621
|
71 if (! ihc_coeffs_.just_hwr_) {
|
alexbrandmeyer@621
|
72 ihc_state_.ac_coupler_ = FloatArray::Zero(n_ch_);
|
alexbrandmeyer@621
|
73 ihc_state_.lpf1_state_ = ihc_coeffs_.rest_output_ * FloatArray::Ones(n_ch_);
|
alexbrandmeyer@621
|
74 ihc_state_.lpf2_state_ = ihc_coeffs_.rest_output_ * FloatArray::Ones(n_ch_);
|
alexbrandmeyer@621
|
75 if (ihc_coeffs_.one_cap_) {
|
alexbrandmeyer@621
|
76 ihc_state_.cap1_voltage_ = ihc_coeffs_.rest_cap1_ *
|
alexbrandmeyer@668
|
77 FloatArray::Ones(n_ch_);
|
alexbrandmeyer@621
|
78 } else {
|
alexbrandmeyer@621
|
79 ihc_state_.cap1_voltage_ = ihc_coeffs_.rest_cap1_ *
|
alexbrandmeyer@668
|
80 FloatArray::Ones(n_ch_);
|
alexbrandmeyer@621
|
81 ihc_state_.cap2_voltage_ = ihc_coeffs_.rest_cap2_ *
|
alexbrandmeyer@668
|
82 FloatArray::Ones(n_ch_);
|
alexbrandmeyer@621
|
83 }
|
alexbrandmeyer@621
|
84 }
|
alexbrandmeyer@621
|
85 }
|
alexbrandmeyer@621
|
86
|
alexbrandmeyer@621
|
87 void Ear::InitAGCState() {
|
alexbrandmeyer@668
|
88 int n_agc_stages = agc_coeffs_.size();
|
alexbrandmeyer@668
|
89 agc_state_.resize(n_agc_stages);
|
alexbrandmeyer@668
|
90 for (int i = 0; i < n_agc_stages; ++i) {
|
alexbrandmeyer@668
|
91 agc_state_[i].decim_phase_ = 0;
|
alexbrandmeyer@668
|
92 agc_state_[i].agc_memory_ = FloatArray::Zero(n_ch_);
|
alexbrandmeyer@668
|
93 agc_state_[i].input_accum_ = FloatArray::Zero(n_ch_);
|
alexbrandmeyer@621
|
94 }
|
alexbrandmeyer@621
|
95 }
|
alexbrandmeyer@621
|
96
|
alexbrandmeyer@668
|
97 void Ear::CARStep(const FPType input, FloatArray* car_out) {
|
alexbrandmeyer@668
|
98 // This interpolates g.
|
alexbrandmeyer@668
|
99 car_state_.g_memory_ = car_state_.g_memory_ + car_state_.dg_memory_;
|
alexbrandmeyer@621
|
100 // This calculates the AGC interpolation state.
|
alexbrandmeyer@668
|
101 car_state_.zb_memory_ = car_state_.zb_memory_ + car_state_.dzb_memory_;
|
alexbrandmeyer@621
|
102 // This updates the nonlinear function of 'velocity' along with zA, which is
|
alexbrandmeyer@621
|
103 // a delay of z2.
|
alexbrandmeyer@668
|
104 FloatArray nonlinear_fun(n_ch_);
|
alexbrandmeyer@668
|
105 FloatArray velocities = car_state_.z2_memory_ - car_state_.za_memory_;
|
alexbrandmeyer@668
|
106 OHCNonlinearFunction(velocities, &nonlinear_fun);
|
alexbrandmeyer@668
|
107 // Here, zb_memory_ * nonlinear_fun is "undamping" delta r.
|
alexbrandmeyer@668
|
108 FloatArray r = car_coeffs_.r1_coeffs_ + (car_state_.zb_memory_ *
|
alexbrandmeyer@668
|
109 nonlinear_fun);
|
alexbrandmeyer@668
|
110 car_state_.za_memory_ = car_state_.z2_memory_;
|
alexbrandmeyer@621
|
111 // Here we reduce the CAR state by r and rotate with the fixed cos/sin coeffs.
|
alexbrandmeyer@668
|
112 FloatArray z1 = r * ((car_coeffs_.a0_coeffs_ * car_state_.z1_memory_) -
|
alexbrandmeyer@668
|
113 (car_coeffs_.c0_coeffs_ * car_state_.z2_memory_));
|
alexbrandmeyer@668
|
114 car_state_.z2_memory_ = r *
|
alexbrandmeyer@668
|
115 ((car_coeffs_.c0_coeffs_ * car_state_.z1_memory_) +
|
alexbrandmeyer@668
|
116 (car_coeffs_.a0_coeffs_ * car_state_.z2_memory_));
|
alexbrandmeyer@668
|
117 car_state_.zy_memory_ = car_coeffs_.h_coeffs_ * car_state_.z2_memory_;
|
alexbrandmeyer@621
|
118 // This section ripples the input-output path, to avoid delay...
|
alexbrandmeyer@621
|
119 // It's the only part that doesn't get computed "in parallel":
|
alexbrandmeyer@668
|
120 FPType in_out = input;
|
alexbrandmeyer@621
|
121 for (int ch = 0; ch < n_ch_; ch++) {
|
alexbrandmeyer@620
|
122 z1(ch) = z1(ch) + in_out;
|
alexbrandmeyer@621
|
123 // This performs the ripple, and saves the final channel outputs in zy.
|
alexbrandmeyer@668
|
124 in_out = car_state_.g_memory_(ch) * (in_out + car_state_.zy_memory_(ch));
|
alexbrandmeyer@668
|
125 car_state_.zy_memory_(ch) = in_out;
|
alexbrandmeyer@620
|
126 }
|
alexbrandmeyer@620
|
127 car_state_.z1_memory_ = z1;
|
alexbrandmeyer@668
|
128 *car_out = car_state_.zy_memory_;
|
alexbrandmeyer@620
|
129 }
|
alexbrandmeyer@620
|
130
|
alexbrandmeyer@621
|
131 // We start with a quadratic nonlinear function, and limit it via a
|
alexbrandmeyer@621
|
132 // rational function. This makes the result go to zero at high
|
alexbrandmeyer@620
|
133 // absolute velocities, so it will do nothing there.
|
alexbrandmeyer@668
|
134 void Ear::OHCNonlinearFunction(const FloatArray& velocities,
|
alexbrandmeyer@668
|
135 FloatArray* nonlinear_fun) {
|
alexbrandmeyer@668
|
136 *nonlinear_fun = (1 + ((velocities * car_coeffs_.velocity_scale_) +
|
alexbrandmeyer@668
|
137 car_coeffs_.v_offset_).square()).inverse();
|
alexbrandmeyer@620
|
138 }
|
alexbrandmeyer@620
|
139
|
alexbrandmeyer@621
|
140 // This step is a one sample-time update of the inner-hair-cell (IHC) model,
|
alexbrandmeyer@621
|
141 // including the detection nonlinearity and either one or two capacitor state
|
alexbrandmeyer@621
|
142 // variables.
|
alexbrandmeyer@668
|
143 void Ear::IHCStep(const FloatArray& car_out, FloatArray* ihc_out) {
|
alexbrandmeyer@668
|
144 FloatArray ac_diff = car_out - ihc_state_.ac_coupler_;
|
alexbrandmeyer@620
|
145 ihc_state_.ac_coupler_ = ihc_state_.ac_coupler_ +
|
alexbrandmeyer@668
|
146 (ihc_coeffs_.ac_coeff_ * ac_diff);
|
alexbrandmeyer@620
|
147 if (ihc_coeffs_.just_hwr_) {
|
alexbrandmeyer@668
|
148 FloatArray output(n_ch_);
|
alexbrandmeyer@668
|
149 for (int ch = 0; ch < n_ch_; ++ch) {
|
alexbrandmeyer@668
|
150 FPType a = (ac_diff(ch) > 0.0) ? ac_diff(ch) : 0.0;
|
alexbrandmeyer@668
|
151 output(ch) = (a < 2) ? a : 2;
|
alexbrandmeyer@620
|
152 }
|
alexbrandmeyer@668
|
153 *ihc_out = output;
|
alexbrandmeyer@620
|
154 } else {
|
alexbrandmeyer@668
|
155 FloatArray conductance = CARFACDetect(ac_diff);
|
alexbrandmeyer@620
|
156 if (ihc_coeffs_.one_cap_) {
|
alexbrandmeyer@668
|
157 *ihc_out = conductance * ihc_state_.cap1_voltage_;
|
alexbrandmeyer@620
|
158 ihc_state_.cap1_voltage_ = ihc_state_.cap1_voltage_ -
|
alexbrandmeyer@668
|
159 (*ihc_out * ihc_coeffs_.out1_rate_) +
|
alexbrandmeyer@668
|
160 ((1 - ihc_state_.cap1_voltage_)
|
alexbrandmeyer@668
|
161 * ihc_coeffs_.in1_rate_);
|
alexbrandmeyer@620
|
162 } else {
|
alexbrandmeyer@668
|
163 *ihc_out = conductance * ihc_state_.cap2_voltage_;
|
alexbrandmeyer@620
|
164 ihc_state_.cap1_voltage_ = ihc_state_.cap1_voltage_ -
|
alexbrandmeyer@668
|
165 ((ihc_state_.cap1_voltage_ - ihc_state_.cap2_voltage_)
|
alexbrandmeyer@668
|
166 * ihc_coeffs_.out1_rate_) +
|
alexbrandmeyer@668
|
167 ((1 - ihc_state_.cap1_voltage_) * ihc_coeffs_.in1_rate_);
|
alexbrandmeyer@620
|
168 ihc_state_.cap2_voltage_ = ihc_state_.cap2_voltage_ -
|
alexbrandmeyer@668
|
169 (*ihc_out * ihc_coeffs_.out2_rate_) +
|
alexbrandmeyer@668
|
170 ((ihc_state_.cap1_voltage_ - ihc_state_.cap2_voltage_)
|
alexbrandmeyer@668
|
171 * ihc_coeffs_.in2_rate_);
|
alexbrandmeyer@620
|
172 }
|
alexbrandmeyer@621
|
173 // Here we smooth the output twice using a LPF.
|
alexbrandmeyer@668
|
174 *ihc_out *= ihc_coeffs_.output_gain_;
|
alexbrandmeyer@620
|
175 ihc_state_.lpf1_state_ = ihc_state_.lpf1_state_ +
|
alexbrandmeyer@668
|
176 (ihc_coeffs_.lpf_coeff_ *
|
alexbrandmeyer@668
|
177 (*ihc_out - ihc_state_.lpf1_state_));
|
alexbrandmeyer@620
|
178 ihc_state_.lpf2_state_ = ihc_state_.lpf2_state_ +
|
alexbrandmeyer@668
|
179 (ihc_coeffs_.lpf_coeff_ *
|
alexbrandmeyer@668
|
180 (ihc_state_.lpf1_state_ - ihc_state_.lpf2_state_));
|
alexbrandmeyer@668
|
181 *ihc_out = ihc_state_.lpf2_state_ - ihc_coeffs_.rest_output_;
|
alexbrandmeyer@620
|
182 }
|
alexbrandmeyer@668
|
183 ihc_state_.ihc_accum_ += *ihc_out;
|
alexbrandmeyer@620
|
184 }
|
alexbrandmeyer@620
|
185
|
alexbrandmeyer@668
|
186 bool Ear::AGCStep(const FloatArray& ihc_out) {
|
alexbrandmeyer@620
|
187 int stage = 0;
|
alexbrandmeyer@668
|
188 int n_stages = agc_coeffs_[0].n_agc_stages_;
|
alexbrandmeyer@668
|
189 FPType detect_scale = agc_coeffs_[n_stages - 1].detect_scale_;
|
alexbrandmeyer@668
|
190 bool updated = AGCRecurse(stage, detect_scale * ihc_out);
|
alexbrandmeyer@620
|
191 return updated;
|
alexbrandmeyer@620
|
192 }
|
alexbrandmeyer@620
|
193
|
alexbrandmeyer@668
|
194 bool Ear::AGCRecurse(const int stage, FloatArray agc_in) {
|
alexbrandmeyer@620
|
195 bool updated = true;
|
alexbrandmeyer@621
|
196 // This is the decim factor for this stage, relative to input or prev. stage:
|
alexbrandmeyer@668
|
197 int decim = agc_coeffs_[stage].decimation_;
|
alexbrandmeyer@621
|
198 // This is the decim phase of this stage (do work on phase 0 only):
|
alexbrandmeyer@668
|
199 int decim_phase = agc_state_[stage].decim_phase_ + 1;
|
alexbrandmeyer@620
|
200 decim_phase = decim_phase % decim;
|
alexbrandmeyer@668
|
201 agc_state_[stage].decim_phase_ = decim_phase;
|
alexbrandmeyer@621
|
202 // Here we accumulate input for this stage from the previous stage:
|
alexbrandmeyer@668
|
203 agc_state_[stage].input_accum_ += agc_in;
|
alexbrandmeyer@621
|
204 // We don't do anything if it's not the right decim_phase.
|
alexbrandmeyer@621
|
205 if (decim_phase == 0) {
|
alexbrandmeyer@621
|
206 // Now we do lots of work, at the decimated rate.
|
alexbrandmeyer@621
|
207 // These are the decimated inputs for this stage, which will be further
|
alexbrandmeyer@621
|
208 // decimated at the next stage.
|
alexbrandmeyer@668
|
209 agc_in = agc_state_[stage].input_accum_ / decim;
|
alexbrandmeyer@621
|
210 // This resets the accumulator.
|
alexbrandmeyer@668
|
211 agc_state_[stage].input_accum_ = FloatArray::Zero(n_ch_);
|
alexbrandmeyer@668
|
212 if (stage < (agc_coeffs_.size() - 1)) {
|
alexbrandmeyer@621
|
213 // Now we recurse to evaluate the next stage(s).
|
alexbrandmeyer@668
|
214 // TODO (alexbrandmeyer): the Matlab version of AGCRecurse can return a
|
alexbrandmeyer@668
|
215 // value for updated, but isn't used in that version. Check with Dick to
|
alexbrandmeyer@668
|
216 // see if that is needed.
|
alexbrandmeyer@621
|
217 updated = AGCRecurse(stage + 1, agc_in);
|
alexbrandmeyer@621
|
218 // Afterwards we add its output to this stage input, whether it updated or
|
alexbrandmeyer@621
|
219 // not.
|
alexbrandmeyer@668
|
220 agc_in += agc_coeffs_[stage].agc_stage_gain_ *
|
alexbrandmeyer@668
|
221 agc_state_[stage + 1].agc_memory_;
|
alexbrandmeyer@620
|
222 }
|
alexbrandmeyer@668
|
223 FloatArray agc_stage_state = agc_state_[stage].agc_memory_;
|
alexbrandmeyer@621
|
224 // This performs a first-order recursive smoothing filter update, in time.
|
alexbrandmeyer@668
|
225 agc_stage_state += agc_coeffs_[stage].agc_epsilon_ *
|
alexbrandmeyer@668
|
226 (agc_in - agc_stage_state);
|
alexbrandmeyer@620
|
227 agc_stage_state = AGCSpatialSmooth(stage, agc_stage_state);
|
alexbrandmeyer@668
|
228 agc_state_[stage].agc_memory_ = agc_stage_state;
|
alexbrandmeyer@668
|
229 updated = true;
|
alexbrandmeyer@620
|
230 } else {
|
alexbrandmeyer@620
|
231 updated = false;
|
alexbrandmeyer@620
|
232 }
|
alexbrandmeyer@620
|
233 return updated;
|
alexbrandmeyer@620
|
234 }
|
alexbrandmeyer@620
|
235
|
alexbrandmeyer@668
|
236 // TODO (alexbrandmeyer): figure out how to operate directly on stage_state.
|
alexbrandmeyer@668
|
237 // Using a pointer breaks the () indexing of the Eigen arrays, but there must
|
alexbrandmeyer@668
|
238 // be a way around this.
|
alexbrandmeyer@668
|
239 FloatArray Ear::AGCSpatialSmooth(const int stage, FloatArray stage_state) {
|
alexbrandmeyer@668
|
240 int n_iterations = agc_coeffs_[stage].agc_spatial_iterations_;
|
alexbrandmeyer@620
|
241 bool use_fir;
|
alexbrandmeyer@621
|
242 use_fir = (n_iterations < 4) ? true : false;
|
alexbrandmeyer@620
|
243 if (use_fir) {
|
alexbrandmeyer@668
|
244 std::vector<FPType> fir_coeffs = agc_coeffs_[stage].agc_spatial_fir_;
|
alexbrandmeyer@620
|
245 FloatArray ss_tap1(n_ch_);
|
alexbrandmeyer@620
|
246 FloatArray ss_tap2(n_ch_);
|
alexbrandmeyer@620
|
247 FloatArray ss_tap3(n_ch_);
|
alexbrandmeyer@620
|
248 FloatArray ss_tap4(n_ch_);
|
alexbrandmeyer@668
|
249 int n_taps = agc_coeffs_[stage].agc_spatial_n_taps_;
|
alexbrandmeyer@621
|
250 // This initializes the first two taps of stage state, which are used for
|
alexbrandmeyer@621
|
251 // both possible cases.
|
alexbrandmeyer@620
|
252 ss_tap1(0) = stage_state(0);
|
alexbrandmeyer@621
|
253 ss_tap1.block(1, 0, n_ch_ - 1, 1) = stage_state.block(0, 0, n_ch_ - 1, 1);
|
alexbrandmeyer@621
|
254 ss_tap2(n_ch_ - 1) = stage_state(n_ch_ - 1);
|
alexbrandmeyer@621
|
255 ss_tap2.block(0, 0, n_ch_ - 1, 1) = stage_state.block(1, 0, n_ch_ - 1, 1);
|
alexbrandmeyer@620
|
256 switch (n_taps) {
|
alexbrandmeyer@620
|
257 case 3:
|
alexbrandmeyer@668
|
258 stage_state = (fir_coeffs[0] * ss_tap1) +
|
alexbrandmeyer@668
|
259 (fir_coeffs[1] * stage_state) +
|
alexbrandmeyer@668
|
260 (fir_coeffs[2] * ss_tap2);
|
alexbrandmeyer@620
|
261 break;
|
alexbrandmeyer@620
|
262 case 5:
|
alexbrandmeyer@621
|
263 // Now we initialize last two taps of stage state, which are only used
|
alexbrandmeyer@621
|
264 // for the 5-tap case.
|
alexbrandmeyer@620
|
265 ss_tap3(0) = stage_state(0);
|
alexbrandmeyer@620
|
266 ss_tap3(1) = stage_state(1);
|
alexbrandmeyer@621
|
267 ss_tap3.block(2, 0, n_ch_ - 2, 1) =
|
alexbrandmeyer@668
|
268 stage_state.block(0, 0, n_ch_ - 2, 1);
|
alexbrandmeyer@621
|
269 ss_tap4(n_ch_ - 2) = stage_state(n_ch_ - 1);
|
alexbrandmeyer@621
|
270 ss_tap4(n_ch_ - 1) = stage_state(n_ch_ - 2);
|
alexbrandmeyer@621
|
271 ss_tap4.block(0, 0, n_ch_ - 2, 1) =
|
alexbrandmeyer@668
|
272 stage_state.block(2, 0, n_ch_ - 2, 1);
|
alexbrandmeyer@668
|
273 stage_state = (fir_coeffs[0] * (ss_tap3 + ss_tap1)) +
|
alexbrandmeyer@668
|
274 (fir_coeffs[1] * stage_state) +
|
alexbrandmeyer@668
|
275 (fir_coeffs[2] * (ss_tap2 + ss_tap4));
|
alexbrandmeyer@620
|
276 break;
|
alexbrandmeyer@620
|
277 default:
|
alexbrandmeyer@622
|
278 break;
|
alexbrandmeyer@668
|
279 CHECK_EQ(5, n_taps) <<
|
alexbrandmeyer@668
|
280 "Bad n_taps in AGCSpatialSmooth; should be 3 or 5.";
|
alexbrandmeyer@620
|
281 }
|
alexbrandmeyer@620
|
282 } else {
|
alexbrandmeyer@621
|
283 stage_state = AGCSmoothDoubleExponential(stage_state,
|
alexbrandmeyer@668
|
284 agc_coeffs_[stage].agc_pole_z1_,
|
alexbrandmeyer@668
|
285 agc_coeffs_[stage].agc_pole_z2_);
|
alexbrandmeyer@620
|
286 }
|
alexbrandmeyer@620
|
287 return stage_state;
|
alexbrandmeyer@620
|
288 }
|
alexbrandmeyer@620
|
289
|
alexbrandmeyer@668
|
290 // TODO (alexbrandmeyer): figure out how to operate directly on stage_state.
|
alexbrandmeyer@668
|
291 // Same point as above for AGCSpatialSmooth.
|
alexbrandmeyer@621
|
292 FloatArray Ear::AGCSmoothDoubleExponential(FloatArray stage_state,
|
alexbrandmeyer@668
|
293 const FPType pole_z1,
|
alexbrandmeyer@668
|
294 const FPType pole_z2) {
|
alexbrandmeyer@622
|
295 int32_t n_pts = stage_state.size();
|
alexbrandmeyer@621
|
296 FPType input;
|
alexbrandmeyer@622
|
297 FPType state = 0.0;
|
alexbrandmeyer@668
|
298 // TODO (alexbrandmeyer): I'm assuming one dimensional input for now, but this
|
alexbrandmeyer@621
|
299 // should be verified with Dick for the final version
|
alexbrandmeyer@668
|
300 for (int i = n_pts - 11; i < n_pts; ++i){
|
alexbrandmeyer@621
|
301 input = stage_state(i);
|
alexbrandmeyer@621
|
302 state = state + (1 - pole_z1) * (input - state);
|
alexbrandmeyer@621
|
303 }
|
alexbrandmeyer@668
|
304 for (int i = n_pts - 1; i > -1; --i){
|
alexbrandmeyer@621
|
305 input = stage_state(i);
|
alexbrandmeyer@621
|
306 state = state + (1 - pole_z2) * (input - state);
|
alexbrandmeyer@621
|
307 }
|
alexbrandmeyer@668
|
308 for (int i = 0; i < n_pts; ++i){
|
alexbrandmeyer@621
|
309 input = stage_state(i);
|
alexbrandmeyer@621
|
310 state = state + (1 - pole_z1) * (input - state);
|
alexbrandmeyer@621
|
311 stage_state(i) = state;
|
alexbrandmeyer@621
|
312 }
|
alexbrandmeyer@620
|
313 return stage_state;
|
alexbrandmeyer@620
|
314 }
|
alexbrandmeyer@621
|
315
|
alexbrandmeyer@668
|
316 FloatArray Ear::StageGValue(const FloatArray& undamping) {
|
alexbrandmeyer@668
|
317 FloatArray r = car_coeffs_.r1_coeffs_ + car_coeffs_.zr_coeffs_ * undamping;
|
alexbrandmeyer@668
|
318 return (1 - 2 * r * car_coeffs_.a0_coeffs_ + (r * r)) /
|
alexbrandmeyer@668
|
319 (1 - 2 * r * car_coeffs_.a0_coeffs_ +
|
alexbrandmeyer@668
|
320 car_coeffs_.h_coeffs_ * r * car_coeffs_.c0_coeffs_ + (r * r));
|
alexbrandmeyer@668
|
321 } |