annotate carfac/ear.cc @ 610:01986636257a

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