annotate trunk/carfac/ear.cc @ 668:933cf18d9a59

Fourth revision of Alex Brandmeyer's C++ implementation. Fixed more style issues, changed AGC structures to vectors, replaced FloatArray2d with vector<FloatArray>, implemented first tests using GTest to verify coefficients and monaural output against Matlab values (stored in aimc/carfac/test_data/). To run tests, change the path stored in carfac_test.h in TEST_SRC_DIR. Added CARFAC_GenerateTestData to the Matlab branch, fixed stage indexing in CARFAC_Cross_Couple.m to reflect changes in AGCCoeffs and AGCState structs.
author alexbrandmeyer
date Wed, 22 May 2013 21:30:02 +0000
parents 6ec6b50f13da
children 443b522fb593
rev   line source
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 }