annotate trunk/carfac/ear.cc @ 622:16918ffbf975

Carfac C++ revision 3, indluding more style improvements. The output structs are now classes again, and have separate storage methods for each output structure along with flags in the Run and RunSegment methods to allow for only storing NAPs if desired.
author alexbrandmeyer
date Fri, 17 May 2013 19:52:45 +0000
parents d763637a05c5
children 6ec6b50f13da
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@621 27 // on a single audio channel.
alexbrandmeyer@622 28 void Ear::InitEar(int n_ch, int32_t fs, FloatArray pole_freqs,
alexbrandmeyer@621 29 CARParams car_p, IHCParams ihc_p, AGCParams agc_p) {
alexbrandmeyer@621 30 // The first section of code determines the number of channels that will be
alexbrandmeyer@621 31 // used in the model on the basis of the sample rate and the CAR parameters
alexbrandmeyer@621 32 // that have been passed to this function.
alexbrandmeyer@621 33 n_ch_ = n_ch;
alexbrandmeyer@621 34 // These functions use the parameters that have been passed to design the
alexbrandmeyer@621 35 // coefficients for the three stages of the model.
alexbrandmeyer@621 36 DesignFilters(car_p, fs, pole_freqs);
alexbrandmeyer@621 37 DesignIHC(ihc_p, fs);
alexbrandmeyer@621 38 DesignAGC(agc_p, fs);
alexbrandmeyer@621 39 // Once the coefficients have been determined, we can initialize the state
alexbrandmeyer@621 40 // variables that will be used during runtime.
alexbrandmeyer@621 41 InitCARState();
alexbrandmeyer@621 42 InitIHCState();
alexbrandmeyer@621 43 InitAGCState();
alexbrandmeyer@620 44 }
alexbrandmeyer@620 45
alexbrandmeyer@622 46 void Ear::DesignFilters(CARParams car_params, int32_t fs,
alexbrandmeyer@621 47 FloatArray pole_freqs) {
alexbrandmeyer@621 48 car_coeffs_.velocity_scale_ = car_params.velocity_scale_;
alexbrandmeyer@621 49 car_coeffs_.v_offset_ = car_params.v_offset_;
alexbrandmeyer@621 50 car_coeffs_.r1_coeffs_.resize(n_ch_);
alexbrandmeyer@621 51 car_coeffs_.a0_coeffs_.resize(n_ch_);
alexbrandmeyer@621 52 car_coeffs_.c0_coeffs_.resize(n_ch_);
alexbrandmeyer@621 53 car_coeffs_.h_coeffs_.resize(n_ch_);
alexbrandmeyer@621 54 car_coeffs_.g0_coeffs_.resize(n_ch_);
alexbrandmeyer@621 55 FPType f = car_params.zero_ratio_ * car_params.zero_ratio_ - 1;
alexbrandmeyer@621 56 FloatArray theta = pole_freqs * ((2 * PI) / fs);
alexbrandmeyer@621 57 car_coeffs_.c0_coeffs_ = sin(theta);
alexbrandmeyer@621 58 car_coeffs_.a0_coeffs_ = cos(theta);
alexbrandmeyer@621 59 FPType ff = car_params.high_f_damping_compression_;
alexbrandmeyer@621 60 FloatArray x = theta / PI;
alexbrandmeyer@621 61 car_coeffs_.zr_coeffs_ = PI * (x - (ff * (x * x * x)));
alexbrandmeyer@621 62 FPType max_zeta = car_params.max_zeta_;
alexbrandmeyer@621 63 FPType min_zeta = car_params.min_zeta_;
alexbrandmeyer@621 64 car_coeffs_.r1_coeffs_ = (1 - (car_coeffs_.zr_coeffs_ * max_zeta));
alexbrandmeyer@621 65 FPType curfreq;
alexbrandmeyer@621 66 FloatArray erb_freqs(n_ch_);
alexbrandmeyer@621 67 for (int ch=0; ch < n_ch_; ch++) {
alexbrandmeyer@621 68 curfreq = pole_freqs(ch);
alexbrandmeyer@621 69 erb_freqs(ch) = ERBHz(curfreq, car_params.erb_break_freq_,
alexbrandmeyer@621 70 car_params.erb_q_);
alexbrandmeyer@621 71 }
alexbrandmeyer@621 72 FloatArray min_zetas = min_zeta + (0.25 * ((erb_freqs / pole_freqs) - min_zeta));
alexbrandmeyer@621 73 car_coeffs_.zr_coeffs_ *= max_zeta - min_zetas;
alexbrandmeyer@621 74 car_coeffs_.h_coeffs_ = car_coeffs_.c0_coeffs_ * f;
alexbrandmeyer@621 75 FloatArray relative_undamping = FloatArray::Ones(n_ch_);
alexbrandmeyer@621 76 FloatArray r = car_coeffs_.r1_coeffs_ +
alexbrandmeyer@621 77 (car_coeffs_.zr_coeffs_ * relative_undamping);
alexbrandmeyer@621 78 car_coeffs_.g0_coeffs_ = (1 - (2 * r * car_coeffs_.a0_coeffs_) + (r * r)) /
alexbrandmeyer@621 79 (1 - (2 * r * car_coeffs_.a0_coeffs_) +
alexbrandmeyer@621 80 (car_coeffs_.h_coeffs_ * r * car_coeffs_.c0_coeffs_) +
alexbrandmeyer@621 81 (r * r));
alexbrandmeyer@621 82 }
alexbrandmeyer@621 83
alexbrandmeyer@621 84
alexbrandmeyer@622 85 void Ear::DesignAGC(AGCParams agc_params, int32_t fs) {
alexbrandmeyer@621 86 // These data members could probably be initialized locally within the design
alexbrandmeyer@621 87 // function.
alexbrandmeyer@621 88 agc_coeffs_.n_agc_stages_ = agc_params.n_stages_;
alexbrandmeyer@621 89 agc_coeffs_.agc_stage_gain_ = agc_params.agc_stage_gain_;
alexbrandmeyer@621 90 FloatArray agc1_scales = agc_params.agc1_scales_;
alexbrandmeyer@621 91 FloatArray agc2_scales = agc_params.agc2_scales_;
alexbrandmeyer@621 92 FloatArray time_constants = agc_params.time_constants_;
alexbrandmeyer@621 93 agc_coeffs_.agc_epsilon_.resize(agc_coeffs_.n_agc_stages_);
alexbrandmeyer@621 94 agc_coeffs_.agc_pole_z1_.resize(agc_coeffs_.n_agc_stages_);
alexbrandmeyer@621 95 agc_coeffs_.agc_pole_z2_.resize(agc_coeffs_.n_agc_stages_);
alexbrandmeyer@621 96 agc_coeffs_.agc_spatial_iterations_.resize(agc_coeffs_.n_agc_stages_);
alexbrandmeyer@621 97 agc_coeffs_.agc_spatial_n_taps_.resize(agc_coeffs_.n_agc_stages_);
alexbrandmeyer@621 98 agc_coeffs_.agc_spatial_fir_.resize(3,agc_coeffs_.n_agc_stages_);
alexbrandmeyer@621 99 agc_coeffs_.agc_mix_coeffs_.resize(agc_coeffs_.n_agc_stages_);
alexbrandmeyer@621 100 FPType mix_coeff = agc_params.agc_mix_coeff_;
alexbrandmeyer@621 101 int decim = 1;
alexbrandmeyer@621 102 agc_coeffs_.decimation_ = agc_params.decimation_;
alexbrandmeyer@622 103 FPType total_dc_gain = 0.0;
alexbrandmeyer@621 104 // Here we loop through each of the stages of the AGC.
alexbrandmeyer@621 105 for (int stage=0; stage < agc_coeffs_.n_agc_stages_; stage++) {
alexbrandmeyer@621 106 FPType tau = time_constants(stage);
alexbrandmeyer@621 107 decim *= agc_coeffs_.decimation_.at(stage);
alexbrandmeyer@621 108 agc_coeffs_.agc_epsilon_(stage) = 1 - exp((-1 * decim) / (tau * fs));
alexbrandmeyer@621 109 FPType n_times = tau * (fs / decim);
alexbrandmeyer@621 110 FPType delay = (agc2_scales(stage) - agc1_scales(stage)) / n_times;
alexbrandmeyer@621 111 FPType spread_sq = (pow(agc1_scales(stage), 2) +
alexbrandmeyer@621 112 pow(agc2_scales(stage), 2)) / n_times;
alexbrandmeyer@621 113 FPType u = 1 + (1 / spread_sq);
alexbrandmeyer@621 114 FPType p = u - sqrt(pow(u, 2) - 1);
alexbrandmeyer@621 115 FPType dp = delay * (1 - (2 * p) + (p * p)) / 2;
alexbrandmeyer@621 116 FPType pole_z1 = p - dp;
alexbrandmeyer@621 117 FPType pole_z2 = p + dp;
alexbrandmeyer@621 118 agc_coeffs_.agc_pole_z1_(stage) = pole_z1;
alexbrandmeyer@621 119 agc_coeffs_.agc_pole_z2_(stage) = pole_z2;
alexbrandmeyer@621 120 int n_taps = 0;
alexbrandmeyer@621 121 bool fir_ok = false;
alexbrandmeyer@621 122 int n_iterations = 1;
alexbrandmeyer@621 123 // This section initializes the FIR coeffs settings at each stage.
alexbrandmeyer@621 124 FloatArray fir(3);
alexbrandmeyer@621 125 while (! fir_ok) {
alexbrandmeyer@621 126 switch (n_taps) {
alexbrandmeyer@621 127 case 0:
alexbrandmeyer@621 128 n_taps = 3;
alexbrandmeyer@621 129 break;
alexbrandmeyer@621 130 case 3:
alexbrandmeyer@621 131 n_taps = 5;
alexbrandmeyer@621 132 break;
alexbrandmeyer@621 133 case 5:
alexbrandmeyer@621 134 n_iterations ++;
alexbrandmeyer@621 135 if (n_iterations > 16){
alexbrandmeyer@621 136 // This implies too many iterations, so we shoud indicate and error.
alexbrandmeyer@621 137 // TODO alexbrandmeyer, proper Google error handling.
alexbrandmeyer@621 138 }
alexbrandmeyer@621 139 break;
alexbrandmeyer@621 140 default:
alexbrandmeyer@621 141 // This means a bad n_taps has been provided, so there should again be
alexbrandmeyer@621 142 // an error.
alexbrandmeyer@621 143 // TODO alexbrandmeyer, proper Google error handling.
alexbrandmeyer@621 144 break;
alexbrandmeyer@621 145 }
alexbrandmeyer@621 146 // Now we can design the FIR coefficients.
alexbrandmeyer@621 147 FPType var = spread_sq / n_iterations;
alexbrandmeyer@621 148 FPType mn = delay / n_iterations;
alexbrandmeyer@621 149 FPType a, b;
alexbrandmeyer@621 150 switch (n_taps) {
alexbrandmeyer@621 151 case 3:
alexbrandmeyer@621 152 a = (var + pow(mn, 2) - mn) / 2;
alexbrandmeyer@621 153 b = (var + pow(mn, 2) + mn) / 2;
alexbrandmeyer@622 154 fir(0) = a;
alexbrandmeyer@622 155 fir(1) = 1 - a - b;
alexbrandmeyer@622 156 fir(2) = b;
alexbrandmeyer@621 157 fir_ok = (fir(2) >= 0.2) ? true : false;
alexbrandmeyer@621 158 break;
alexbrandmeyer@621 159 case 5:
alexbrandmeyer@621 160 a = (((var + pow(mn, 2)) * 2/5) - (mn * 2/3)) / 2;
alexbrandmeyer@621 161 b = (((var + pow(mn, 2)) * 2/5) + (mn * 2/3)) / 2;
alexbrandmeyer@622 162 fir(0) = a / 2;
alexbrandmeyer@622 163 fir(1) = 1 - a - b;
alexbrandmeyer@622 164 fir(2) = b / 2;
alexbrandmeyer@621 165 fir_ok = (fir(2) >= 0.1) ? true : false;
alexbrandmeyer@621 166 break;
alexbrandmeyer@621 167 default:
alexbrandmeyer@621 168 break; // Again, we've arrived at a bad n_taps in the design.
alexbrandmeyer@621 169 // TODO alexbrandmeyer. Add proper Google error handling.
alexbrandmeyer@621 170 }
alexbrandmeyer@621 171 }
alexbrandmeyer@621 172 // Once we have the FIR design for this stage we can assign it to the
alexbrandmeyer@621 173 // appropriate data members.
alexbrandmeyer@621 174 agc_coeffs_.agc_spatial_iterations_(stage) = n_iterations;
alexbrandmeyer@621 175 agc_coeffs_.agc_spatial_n_taps_(stage) = n_taps;
alexbrandmeyer@621 176 // TODO alexbrandmeyer replace using Eigen block method
alexbrandmeyer@621 177 agc_coeffs_.agc_spatial_fir_(0, stage) = fir(0);
alexbrandmeyer@621 178 agc_coeffs_.agc_spatial_fir_(1, stage) = fir(1);
alexbrandmeyer@621 179 agc_coeffs_.agc_spatial_fir_(2, stage) = fir(2);
alexbrandmeyer@621 180 total_dc_gain += pow(agc_coeffs_.agc_stage_gain_, (stage));
alexbrandmeyer@621 181 agc_coeffs_.agc_mix_coeffs_(stage) =
alexbrandmeyer@621 182 (stage == 0) ? 0 : mix_coeff / (tau * (fs /decim));
alexbrandmeyer@621 183 }
alexbrandmeyer@621 184 agc_coeffs_.agc_gain_ = total_dc_gain;
alexbrandmeyer@621 185 agc_coeffs_.detect_scale_ = 1 / total_dc_gain;
alexbrandmeyer@621 186 }
alexbrandmeyer@621 187
alexbrandmeyer@622 188 void Ear::DesignIHC(IHCParams ihc_params, int32_t fs) {
alexbrandmeyer@621 189 if (ihc_params.just_hwr_) {
alexbrandmeyer@621 190 ihc_coeffs_.just_hwr_ = ihc_params.just_hwr_;
alexbrandmeyer@621 191 } else {
alexbrandmeyer@621 192 // This section calculates conductance values using two pre-defined scalars.
alexbrandmeyer@621 193 FloatArray x(1);
alexbrandmeyer@621 194 FPType conduct_at_10, conduct_at_0;
alexbrandmeyer@622 195 x(0) = 10.0;
alexbrandmeyer@621 196 x = CARFACDetect(x);
alexbrandmeyer@621 197 conduct_at_10 = x(0);
alexbrandmeyer@622 198 x(0) = 0.0;
alexbrandmeyer@621 199 x = CARFACDetect(x);
alexbrandmeyer@621 200 conduct_at_0 = x(0);
alexbrandmeyer@621 201 if (ihc_params.one_cap_) {
alexbrandmeyer@621 202 FPType ro = 1 / conduct_at_10 ;
alexbrandmeyer@621 203 FPType c = ihc_params.tau1_out_ / ro;
alexbrandmeyer@621 204 FPType ri = ihc_params.tau1_in_ / c;
alexbrandmeyer@621 205 FPType saturation_output = 1 / ((2 * ro) + ri);
alexbrandmeyer@621 206 FPType r0 = 1 / conduct_at_0;
alexbrandmeyer@621 207 FPType current = 1 / (ri + r0);
alexbrandmeyer@621 208 ihc_coeffs_.cap1_voltage_ = 1 - (current * ri);
alexbrandmeyer@621 209 ihc_coeffs_.just_hwr_ = false;
alexbrandmeyer@621 210 ihc_coeffs_.lpf_coeff_ = 1 - exp( -1 / (ihc_params.tau_lpf_ * fs));
alexbrandmeyer@621 211 ihc_coeffs_.out1_rate_ = ro / (ihc_params.tau1_out_ * fs);
alexbrandmeyer@621 212 ihc_coeffs_.in1_rate_ = 1 / (ihc_params.tau1_in_ * fs);
alexbrandmeyer@621 213 ihc_coeffs_.one_cap_ = ihc_params.one_cap_;
alexbrandmeyer@621 214 ihc_coeffs_.output_gain_ = 1 / (saturation_output - current);
alexbrandmeyer@621 215 ihc_coeffs_.rest_output_ = current / (saturation_output - current);
alexbrandmeyer@621 216 ihc_coeffs_.rest_cap1_ = ihc_coeffs_.cap1_voltage_;
alexbrandmeyer@621 217 } else {
alexbrandmeyer@621 218 FPType ro = 1 / conduct_at_10;
alexbrandmeyer@621 219 FPType c2 = ihc_params.tau2_out_ / ro;
alexbrandmeyer@621 220 FPType r2 = ihc_params.tau2_in_ / c2;
alexbrandmeyer@621 221 FPType c1 = ihc_params.tau1_out_ / r2;
alexbrandmeyer@621 222 FPType r1 = ihc_params.tau1_in_ / c1;
alexbrandmeyer@621 223 FPType saturation_output = 1 / (2 * ro + r2 + r1);
alexbrandmeyer@621 224 FPType r0 = 1 / conduct_at_0;
alexbrandmeyer@621 225 FPType current = 1 / (r1 + r2 + r0);
alexbrandmeyer@621 226 ihc_coeffs_.cap1_voltage_ = 1 - (current * r1);
alexbrandmeyer@621 227 ihc_coeffs_.cap2_voltage_ = ihc_coeffs_.cap1_voltage_ - (current * r2);
alexbrandmeyer@621 228 ihc_coeffs_.just_hwr_ = false;
alexbrandmeyer@621 229 ihc_coeffs_.lpf_coeff_ = 1 - exp(-1 / (ihc_params.tau_lpf_ * fs));
alexbrandmeyer@621 230 ihc_coeffs_.out1_rate_ = 1 / (ihc_params.tau1_out_ * fs);
alexbrandmeyer@621 231 ihc_coeffs_.in1_rate_ = 1 / (ihc_params.tau1_in_ * fs);
alexbrandmeyer@621 232 ihc_coeffs_.out2_rate_ = ro / (ihc_params.tau2_out_ * fs);
alexbrandmeyer@621 233 ihc_coeffs_.in2_rate_ = 1 / (ihc_params.tau2_in_ * fs);
alexbrandmeyer@621 234 ihc_coeffs_.one_cap_ = ihc_params.one_cap_;
alexbrandmeyer@621 235 ihc_coeffs_.output_gain_ = 1 / (saturation_output - current);
alexbrandmeyer@621 236 ihc_coeffs_.rest_output_ = current / (saturation_output - current);
alexbrandmeyer@621 237 ihc_coeffs_.rest_cap1_ = ihc_coeffs_.cap1_voltage_;
alexbrandmeyer@621 238 ihc_coeffs_.rest_cap2_ = ihc_coeffs_.cap2_voltage_;
alexbrandmeyer@621 239 }
alexbrandmeyer@621 240 }
alexbrandmeyer@621 241 ihc_coeffs_.ac_coeff_ = 2 * PI * ihc_params.ac_corner_hz_ / fs;
alexbrandmeyer@621 242 }
alexbrandmeyer@621 243
alexbrandmeyer@621 244 void Ear::InitCARState() {
alexbrandmeyer@621 245 car_state_.z1_memory_ = FloatArray::Zero(n_ch_);
alexbrandmeyer@621 246 car_state_.z2_memory_ = FloatArray::Zero(n_ch_);
alexbrandmeyer@621 247 car_state_.za_memory_ = FloatArray::Zero(n_ch_);
alexbrandmeyer@621 248 car_state_.zb_memory_ = car_coeffs_.zr_coeffs_;
alexbrandmeyer@621 249 car_state_.dzb_memory_ = FloatArray::Zero(n_ch_);
alexbrandmeyer@621 250 car_state_.zy_memory_ = FloatArray::Zero(n_ch_);
alexbrandmeyer@621 251 car_state_.g_memory_ = car_coeffs_.g0_coeffs_;
alexbrandmeyer@621 252 car_state_.dg_memory_ = FloatArray::Zero(n_ch_);
alexbrandmeyer@621 253 }
alexbrandmeyer@621 254
alexbrandmeyer@621 255 void Ear::InitIHCState() {
alexbrandmeyer@621 256 ihc_state_.ihc_accum_ = FloatArray::Zero(n_ch_);
alexbrandmeyer@621 257 if (! ihc_coeffs_.just_hwr_) {
alexbrandmeyer@621 258 ihc_state_.ac_coupler_ = FloatArray::Zero(n_ch_);
alexbrandmeyer@621 259 ihc_state_.lpf1_state_ = ihc_coeffs_.rest_output_ * FloatArray::Ones(n_ch_);
alexbrandmeyer@621 260 ihc_state_.lpf2_state_ = ihc_coeffs_.rest_output_ * FloatArray::Ones(n_ch_);
alexbrandmeyer@621 261 if (ihc_coeffs_.one_cap_) {
alexbrandmeyer@621 262 ihc_state_.cap1_voltage_ = ihc_coeffs_.rest_cap1_ *
alexbrandmeyer@621 263 FloatArray::Ones(n_ch_);
alexbrandmeyer@621 264 } else {
alexbrandmeyer@621 265 ihc_state_.cap1_voltage_ = ihc_coeffs_.rest_cap1_ *
alexbrandmeyer@621 266 FloatArray::Ones(n_ch_);
alexbrandmeyer@621 267 ihc_state_.cap2_voltage_ = ihc_coeffs_.rest_cap2_ *
alexbrandmeyer@621 268 FloatArray::Ones(n_ch_);
alexbrandmeyer@621 269 }
alexbrandmeyer@621 270 }
alexbrandmeyer@621 271 }
alexbrandmeyer@621 272
alexbrandmeyer@621 273 void Ear::InitAGCState() {
alexbrandmeyer@621 274 agc_state_.n_agc_stages_ = agc_coeffs_.n_agc_stages_;
alexbrandmeyer@621 275 agc_state_.agc_memory_.resize(agc_state_.n_agc_stages_);
alexbrandmeyer@621 276 agc_state_.input_accum_.resize(agc_state_.n_agc_stages_);
alexbrandmeyer@621 277 agc_state_.decim_phase_.resize(agc_state_.n_agc_stages_);
alexbrandmeyer@621 278 for (int i = 0; i < agc_state_.n_agc_stages_; i++) {
alexbrandmeyer@621 279 agc_state_.decim_phase_.at(i) = 0;
alexbrandmeyer@621 280 agc_state_.agc_memory_.at(i) = FloatArray::Zero(n_ch_);
alexbrandmeyer@621 281 agc_state_.input_accum_.at(i) = FloatArray::Zero(n_ch_);
alexbrandmeyer@621 282 }
alexbrandmeyer@621 283 }
alexbrandmeyer@621 284
alexbrandmeyer@621 285 FloatArray Ear::CARStep(FPType input) {
alexbrandmeyer@620 286 FloatArray g(n_ch_);
alexbrandmeyer@620 287 FloatArray zb(n_ch_);
alexbrandmeyer@620 288 FloatArray za(n_ch_);
alexbrandmeyer@620 289 FloatArray v(n_ch_);
alexbrandmeyer@620 290 FloatArray nlf(n_ch_);
alexbrandmeyer@620 291 FloatArray r(n_ch_);
alexbrandmeyer@620 292 FloatArray z1(n_ch_);
alexbrandmeyer@620 293 FloatArray z2(n_ch_);
alexbrandmeyer@620 294 FloatArray zy(n_ch_);
alexbrandmeyer@620 295 FPType in_out;
alexbrandmeyer@621 296 // This performs the DOHC stuff.
alexbrandmeyer@621 297 g = car_state_.g_memory_ + car_state_.dg_memory_; // This interpolates g.
alexbrandmeyer@621 298 // This calculates the AGC interpolation state.
alexbrandmeyer@621 299 zb = car_state_.zb_memory_ + car_state_.dzb_memory_;
alexbrandmeyer@621 300 // This updates the nonlinear function of 'velocity' along with zA, which is
alexbrandmeyer@621 301 // a delay of z2.
alexbrandmeyer@620 302 za = car_state_.za_memory_;
alexbrandmeyer@620 303 v = car_state_.z2_memory_ - za;
alexbrandmeyer@620 304 nlf = OHC_NLF(v);
alexbrandmeyer@621 305 // Here, zb * nfl is "undamping" delta r.
alexbrandmeyer@621 306 r = car_coeffs_.r1_coeffs_ + (zb * nlf);
alexbrandmeyer@620 307 za = car_state_.z2_memory_;
alexbrandmeyer@621 308 // Here we reduce the CAR state by r and rotate with the fixed cos/sin coeffs.
alexbrandmeyer@620 309 z1 = r * ((car_coeffs_.a0_coeffs_ * car_state_.z1_memory_) -
alexbrandmeyer@620 310 (car_coeffs_.c0_coeffs_ * car_state_.z2_memory_));
alexbrandmeyer@620 311 z2 = r * ((car_coeffs_.c0_coeffs_ * car_state_.z1_memory_) +
alexbrandmeyer@620 312 (car_coeffs_.a0_coeffs_ * car_state_.z2_memory_));
alexbrandmeyer@620 313 zy = car_coeffs_.h_coeffs_ * z2;
alexbrandmeyer@621 314 // This section ripples the input-output path, to avoid delay...
alexbrandmeyer@621 315 // It's the only part that doesn't get computed "in parallel":
alexbrandmeyer@620 316 in_out = input;
alexbrandmeyer@621 317 for (int ch = 0; ch < n_ch_; ch++) {
alexbrandmeyer@620 318 z1(ch) = z1(ch) + in_out;
alexbrandmeyer@621 319 // This performs the ripple, and saves the final channel outputs in zy.
alexbrandmeyer@620 320 in_out = g(ch) * (in_out + zy(ch));
alexbrandmeyer@620 321 zy(ch) = in_out;
alexbrandmeyer@620 322 }
alexbrandmeyer@620 323 car_state_.z1_memory_ = z1;
alexbrandmeyer@620 324 car_state_.z2_memory_ = z2;
alexbrandmeyer@620 325 car_state_.za_memory_ = za;
alexbrandmeyer@620 326 car_state_.zb_memory_ = zb;
alexbrandmeyer@620 327 car_state_.zy_memory_ = zy;
alexbrandmeyer@620 328 car_state_.g_memory_ = g;
alexbrandmeyer@621 329 // The output of the CAR is equal to the zy state.
alexbrandmeyer@620 330 return zy;
alexbrandmeyer@620 331 }
alexbrandmeyer@620 332
alexbrandmeyer@621 333 // We start with a quadratic nonlinear function, and limit it via a
alexbrandmeyer@621 334 // rational function. This makes the result go to zero at high
alexbrandmeyer@620 335 // absolute velocities, so it will do nothing there.
alexbrandmeyer@621 336 FloatArray Ear::OHC_NLF(FloatArray velocities) {
alexbrandmeyer@621 337 return 1 / (1 +
alexbrandmeyer@621 338 (((velocities * car_coeffs_.velocity_scale_) + car_coeffs_.v_offset_) *
alexbrandmeyer@621 339 ((velocities * car_coeffs_.velocity_scale_) + car_coeffs_.v_offset_)));
alexbrandmeyer@621 340
alexbrandmeyer@620 341 }
alexbrandmeyer@620 342
alexbrandmeyer@621 343 // This step is a one sample-time update of the inner-hair-cell (IHC) model,
alexbrandmeyer@621 344 // including the detection nonlinearity and either one or two capacitor state
alexbrandmeyer@621 345 // variables.
alexbrandmeyer@621 346 FloatArray Ear::IHCStep(FloatArray car_out) {
alexbrandmeyer@620 347 FloatArray ihc_out(n_ch_);
alexbrandmeyer@620 348 FloatArray ac_diff(n_ch_);
alexbrandmeyer@620 349 FloatArray conductance(n_ch_);
alexbrandmeyer@620 350 ac_diff = car_out - ihc_state_.ac_coupler_;
alexbrandmeyer@620 351 ihc_state_.ac_coupler_ = ihc_state_.ac_coupler_ +
alexbrandmeyer@620 352 (ihc_coeffs_.ac_coeff_ * ac_diff);
alexbrandmeyer@620 353 if (ihc_coeffs_.just_hwr_) {
alexbrandmeyer@621 354 for (int ch = 0; ch < n_ch_; ch++) {
alexbrandmeyer@620 355 FPType a;
alexbrandmeyer@622 356 a = (ac_diff(ch) > 0.0) ? ac_diff(ch) : 0.0;
alexbrandmeyer@621 357 ihc_out(ch) = (a < 2) ? a : 2;
alexbrandmeyer@620 358 }
alexbrandmeyer@620 359 } else {
alexbrandmeyer@620 360 conductance = CARFACDetect(ac_diff);
alexbrandmeyer@620 361 if (ihc_coeffs_.one_cap_) {
alexbrandmeyer@620 362 ihc_out = conductance * ihc_state_.cap1_voltage_;
alexbrandmeyer@620 363 ihc_state_.cap1_voltage_ = ihc_state_.cap1_voltage_ -
alexbrandmeyer@620 364 (ihc_out * ihc_coeffs_.out1_rate_) +
alexbrandmeyer@620 365 ((1 - ihc_state_.cap1_voltage_)
alexbrandmeyer@620 366 * ihc_coeffs_.in1_rate_);
alexbrandmeyer@620 367 } else {
alexbrandmeyer@620 368 ihc_out = conductance * ihc_state_.cap2_voltage_;
alexbrandmeyer@620 369 ihc_state_.cap1_voltage_ = ihc_state_.cap1_voltage_ -
alexbrandmeyer@620 370 ((ihc_state_.cap1_voltage_ - ihc_state_.cap2_voltage_)
alexbrandmeyer@620 371 * ihc_coeffs_.out1_rate_) +
alexbrandmeyer@620 372 ((1 - ihc_state_.cap1_voltage_) * ihc_coeffs_.in1_rate_);
alexbrandmeyer@620 373 ihc_state_.cap2_voltage_ = ihc_state_.cap2_voltage_ -
alexbrandmeyer@620 374 (ihc_out * ihc_coeffs_.out2_rate_) +
alexbrandmeyer@620 375 ((ihc_state_.cap1_voltage_ - ihc_state_.cap2_voltage_)
alexbrandmeyer@620 376 * ihc_coeffs_.in2_rate_);
alexbrandmeyer@620 377 }
alexbrandmeyer@621 378 // Here we smooth the output twice using a LPF.
alexbrandmeyer@621 379 ihc_out *= ihc_coeffs_.output_gain_;
alexbrandmeyer@620 380 ihc_state_.lpf1_state_ = ihc_state_.lpf1_state_ +
alexbrandmeyer@620 381 (ihc_coeffs_.lpf_coeff_ * (ihc_out - ihc_state_.lpf1_state_));
alexbrandmeyer@620 382 ihc_state_.lpf2_state_ = ihc_state_.lpf2_state_ +
alexbrandmeyer@620 383 (ihc_coeffs_.lpf_coeff_ *
alexbrandmeyer@620 384 (ihc_state_.lpf1_state_ - ihc_state_.lpf2_state_));
alexbrandmeyer@620 385 ihc_out = ihc_state_.lpf2_state_ - ihc_coeffs_.rest_output_;
alexbrandmeyer@620 386 }
alexbrandmeyer@620 387 ihc_state_.ihc_accum_ += ihc_out;
alexbrandmeyer@620 388 return ihc_out;
alexbrandmeyer@620 389 }
alexbrandmeyer@620 390
alexbrandmeyer@621 391 bool Ear::AGCStep(FloatArray ihc_out) {
alexbrandmeyer@620 392 int stage = 0;
alexbrandmeyer@620 393 FloatArray agc_in(n_ch_);
alexbrandmeyer@620 394 agc_in = agc_coeffs_.detect_scale_ * ihc_out;
alexbrandmeyer@620 395 bool updated = AGCRecurse(stage, agc_in);
alexbrandmeyer@620 396 return updated;
alexbrandmeyer@620 397 }
alexbrandmeyer@620 398
alexbrandmeyer@621 399 bool Ear::AGCRecurse(int stage, FloatArray agc_in) {
alexbrandmeyer@620 400 bool updated = true;
alexbrandmeyer@621 401 // This is the decim factor for this stage, relative to input or prev. stage:
alexbrandmeyer@621 402 int decim = agc_coeffs_.decimation_.at(stage);
alexbrandmeyer@621 403 // This is the decim phase of this stage (do work on phase 0 only):
alexbrandmeyer@621 404 int decim_phase = agc_state_.decim_phase_.at(stage);
alexbrandmeyer@620 405 decim_phase = decim_phase % decim;
alexbrandmeyer@621 406 agc_state_.decim_phase_.at(stage) = decim_phase;
alexbrandmeyer@621 407 // Here we accumulate input for this stage from the previous stage:
alexbrandmeyer@621 408 agc_state_.input_accum_.at(stage) += agc_in;
alexbrandmeyer@621 409 // We don't do anything if it's not the right decim_phase.
alexbrandmeyer@621 410 if (decim_phase == 0) {
alexbrandmeyer@621 411 // Now we do lots of work, at the decimated rate.
alexbrandmeyer@621 412 // These are the decimated inputs for this stage, which will be further
alexbrandmeyer@621 413 // decimated at the next stage.
alexbrandmeyer@621 414 agc_in = agc_state_.input_accum_.at(stage) / decim;
alexbrandmeyer@621 415 // This resets the accumulator.
alexbrandmeyer@621 416 agc_state_.input_accum_.at(stage) = FloatArray::Zero(n_ch_);
alexbrandmeyer@621 417 if (stage < (agc_coeffs_.decimation_.size() - 1)) {
alexbrandmeyer@621 418 // Now we recurse to evaluate the next stage(s).
alexbrandmeyer@621 419 updated = AGCRecurse(stage + 1, agc_in);
alexbrandmeyer@621 420 // Afterwards we add its output to this stage input, whether it updated or
alexbrandmeyer@621 421 // not.
alexbrandmeyer@621 422 agc_in += (agc_coeffs_.agc_stage_gain_ *
alexbrandmeyer@621 423 agc_state_.agc_memory_.at(stage + 1));
alexbrandmeyer@620 424 }
alexbrandmeyer@621 425 FloatArray agc_stage_state = agc_state_.agc_memory_.at(stage);
alexbrandmeyer@621 426 // This performs a first-order recursive smoothing filter update, in time.
alexbrandmeyer@620 427 agc_stage_state = agc_stage_state + (agc_coeffs_.agc_epsilon_(stage) *
alexbrandmeyer@620 428 (agc_in - agc_stage_state));
alexbrandmeyer@620 429 agc_stage_state = AGCSpatialSmooth(stage, agc_stage_state);
alexbrandmeyer@621 430 agc_state_.agc_memory_.at(stage) = agc_stage_state;
alexbrandmeyer@620 431 } else {
alexbrandmeyer@620 432 updated = false;
alexbrandmeyer@620 433 }
alexbrandmeyer@620 434 return updated;
alexbrandmeyer@620 435 }
alexbrandmeyer@620 436
alexbrandmeyer@621 437 FloatArray Ear::AGCSpatialSmooth(int stage, FloatArray stage_state) {
alexbrandmeyer@620 438 int n_iterations = agc_coeffs_.agc_spatial_iterations_(stage);
alexbrandmeyer@620 439 bool use_fir;
alexbrandmeyer@621 440 use_fir = (n_iterations < 4) ? true : false;
alexbrandmeyer@620 441 if (use_fir) {
alexbrandmeyer@621 442 FloatArray fir_coeffs = agc_coeffs_.agc_spatial_fir_.block(0, stage, 3, 1);
alexbrandmeyer@620 443 FloatArray ss_tap1(n_ch_);
alexbrandmeyer@620 444 FloatArray ss_tap2(n_ch_);
alexbrandmeyer@620 445 FloatArray ss_tap3(n_ch_);
alexbrandmeyer@620 446 FloatArray ss_tap4(n_ch_);
alexbrandmeyer@620 447 int n_taps = agc_coeffs_.agc_spatial_n_taps_(stage);
alexbrandmeyer@621 448 // This initializes the first two taps of stage state, which are used for
alexbrandmeyer@621 449 // both possible cases.
alexbrandmeyer@620 450 ss_tap1(0) = stage_state(0);
alexbrandmeyer@621 451 ss_tap1.block(1, 0, n_ch_ - 1, 1) = stage_state.block(0, 0, n_ch_ - 1, 1);
alexbrandmeyer@621 452 ss_tap2(n_ch_ - 1) = stage_state(n_ch_ - 1);
alexbrandmeyer@621 453 ss_tap2.block(0, 0, n_ch_ - 1, 1) = stage_state.block(1, 0, n_ch_ - 1, 1);
alexbrandmeyer@620 454 switch (n_taps) {
alexbrandmeyer@620 455 case 3:
alexbrandmeyer@620 456 stage_state = (fir_coeffs(0) * ss_tap1) +
alexbrandmeyer@620 457 (fir_coeffs(1) * stage_state) +
alexbrandmeyer@620 458 (fir_coeffs(2) * ss_tap2);
alexbrandmeyer@620 459 break;
alexbrandmeyer@620 460 case 5:
alexbrandmeyer@621 461 // Now we initialize last two taps of stage state, which are only used
alexbrandmeyer@621 462 // for the 5-tap case.
alexbrandmeyer@620 463 ss_tap3(0) = stage_state(0);
alexbrandmeyer@620 464 ss_tap3(1) = stage_state(1);
alexbrandmeyer@621 465 ss_tap3.block(2, 0, n_ch_ - 2, 1) =
alexbrandmeyer@621 466 stage_state.block(0, 0, n_ch_ - 2, 1);
alexbrandmeyer@621 467 ss_tap4(n_ch_ - 2) = stage_state(n_ch_ - 1);
alexbrandmeyer@621 468 ss_tap4(n_ch_ - 1) = stage_state(n_ch_ - 2);
alexbrandmeyer@621 469 ss_tap4.block(0, 0, n_ch_ - 2, 1) =
alexbrandmeyer@621 470 stage_state.block(2, 0, n_ch_ - 2, 1);
alexbrandmeyer@620 471 stage_state = (fir_coeffs(0) * (ss_tap3 + ss_tap1)) +
alexbrandmeyer@620 472 (fir_coeffs(1) * stage_state) +
alexbrandmeyer@620 473 (fir_coeffs(2) * (ss_tap2 + ss_tap4));
alexbrandmeyer@620 474 break;
alexbrandmeyer@620 475 default:
alexbrandmeyer@622 476 break;
alexbrandmeyer@621 477 // TODO alexbrandmeyer: determine proper error handling implementation.
alexbrandmeyer@620 478 }
alexbrandmeyer@620 479 } else {
alexbrandmeyer@621 480 stage_state = AGCSmoothDoubleExponential(stage_state,
alexbrandmeyer@621 481 agc_coeffs_.agc_pole_z1_(stage),
alexbrandmeyer@621 482 agc_coeffs_.agc_pole_z2_(stage));
alexbrandmeyer@620 483 }
alexbrandmeyer@620 484 return stage_state;
alexbrandmeyer@620 485 }
alexbrandmeyer@620 486
alexbrandmeyer@621 487 FloatArray Ear::AGCSmoothDoubleExponential(FloatArray stage_state,
alexbrandmeyer@621 488 FPType pole_z1, FPType pole_z2) {
alexbrandmeyer@622 489 int32_t n_pts = stage_state.size();
alexbrandmeyer@621 490 FPType input;
alexbrandmeyer@622 491 FPType state = 0.0;
alexbrandmeyer@621 492 // TODO alexbrandmeyer: I'm assuming one dimensional input for now, but this
alexbrandmeyer@621 493 // should be verified with Dick for the final version
alexbrandmeyer@621 494 for (int i = n_pts - 11; i < n_pts; i ++){
alexbrandmeyer@621 495 input = stage_state(i);
alexbrandmeyer@621 496 state = state + (1 - pole_z1) * (input - state);
alexbrandmeyer@621 497 }
alexbrandmeyer@621 498 for (int i = n_pts - 1; i > -1; i --){
alexbrandmeyer@621 499 input = stage_state(i);
alexbrandmeyer@621 500 state = state + (1 - pole_z2) * (input - state);
alexbrandmeyer@621 501 }
alexbrandmeyer@621 502 for (int i = 0; i < n_pts; i ++){
alexbrandmeyer@621 503 input = stage_state(i);
alexbrandmeyer@621 504 state = state + (1 - pole_z1) * (input - state);
alexbrandmeyer@621 505 stage_state(i) = state;
alexbrandmeyer@621 506 }
alexbrandmeyer@620 507 return stage_state;
alexbrandmeyer@620 508 }
alexbrandmeyer@621 509
alexbrandmeyer@621 510 FloatArray Ear::ReturnZAMemory() {
alexbrandmeyer@621 511 return car_state_.za_memory_;
alexbrandmeyer@621 512 }
alexbrandmeyer@621 513
alexbrandmeyer@621 514 FloatArray Ear::ReturnZBMemory() {
alexbrandmeyer@621 515 return car_state_.zb_memory_;
alexbrandmeyer@621 516 }
alexbrandmeyer@621 517
alexbrandmeyer@621 518 FloatArray Ear::ReturnGMemory() {
alexbrandmeyer@621 519 return car_state_.g_memory_;
alexbrandmeyer@621 520 };
alexbrandmeyer@621 521
alexbrandmeyer@621 522 FloatArray Ear::ReturnZRCoeffs() {
alexbrandmeyer@621 523 return car_coeffs_.zr_coeffs_;
alexbrandmeyer@621 524 };
alexbrandmeyer@621 525
alexbrandmeyer@621 526 int Ear::ReturnAGCNStages() {
alexbrandmeyer@621 527 return agc_coeffs_.n_agc_stages_;
alexbrandmeyer@621 528 }
alexbrandmeyer@621 529
alexbrandmeyer@621 530 int Ear::ReturnAGCStateDecimPhase(int stage) {
alexbrandmeyer@621 531 return agc_state_.decim_phase_.at(stage);
alexbrandmeyer@621 532 }
alexbrandmeyer@621 533
alexbrandmeyer@621 534 FPType Ear::ReturnAGCMixCoeff(int stage) {
alexbrandmeyer@621 535 return agc_coeffs_.agc_mix_coeffs_(stage);
alexbrandmeyer@621 536 }
alexbrandmeyer@621 537
alexbrandmeyer@621 538 FloatArray Ear::ReturnAGCStateMemory(int stage) {
alexbrandmeyer@621 539 return agc_state_.agc_memory_.at(stage);
alexbrandmeyer@621 540 }
alexbrandmeyer@621 541
alexbrandmeyer@621 542 int Ear::ReturnAGCDecimation(int stage) {
alexbrandmeyer@621 543 return agc_coeffs_.decimation_.at(stage);
alexbrandmeyer@621 544 }
alexbrandmeyer@621 545
alexbrandmeyer@621 546 void Ear::SetAGCStateMemory(int stage, FloatArray new_values) {
alexbrandmeyer@621 547 agc_state_.agc_memory_.at(stage) = new_values;
alexbrandmeyer@621 548 }
alexbrandmeyer@621 549
alexbrandmeyer@621 550 void Ear::SetCARStateDZBMemory(FloatArray new_values) {
alexbrandmeyer@621 551 car_state_.dzb_memory_ = new_values;
alexbrandmeyer@621 552 }
alexbrandmeyer@621 553
alexbrandmeyer@621 554 void Ear::SetCARStateDGMemory(FloatArray new_values) {
alexbrandmeyer@621 555 car_state_.dg_memory_ = new_values;
alexbrandmeyer@621 556 }
alexbrandmeyer@621 557
alexbrandmeyer@621 558 FloatArray Ear::StageGValue(FloatArray undamping) {
alexbrandmeyer@621 559 FloatArray r1 = car_coeffs_.r1_coeffs_;
alexbrandmeyer@621 560 FloatArray a0 = car_coeffs_.a0_coeffs_;
alexbrandmeyer@621 561 FloatArray c0 = car_coeffs_.c0_coeffs_;
alexbrandmeyer@621 562 FloatArray h = car_coeffs_.h_coeffs_;
alexbrandmeyer@621 563 FloatArray zr = car_coeffs_.zr_coeffs_;
alexbrandmeyer@621 564 FloatArray r = r1 + zr * undamping;
alexbrandmeyer@621 565 return (1 - 2 * r * a0 + (r * r)) / (1 - 2 * r * a0 + h * r * c0 + (r * r));
alexbrandmeyer@621 566 }