annotate trunk/carfac/ear.cc @ 621:d763637a05c5

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 9c268a806bf2
children 16918ffbf975
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@621 28 void Ear::InitEar(int n_ch, long 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@621 46 void Ear::DesignFilters(CARParams car_params, long 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@621 85 void Ear::DesignAGC(AGCParams agc_params, long 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@621 103 FPType total_dc_gain = 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@621 154 fir << a, 1 - a - b, b;
alexbrandmeyer@621 155 fir_ok = (fir(2) >= 0.2) ? true : false;
alexbrandmeyer@621 156 break;
alexbrandmeyer@621 157 case 5:
alexbrandmeyer@621 158 a = (((var + pow(mn, 2)) * 2/5) - (mn * 2/3)) / 2;
alexbrandmeyer@621 159 b = (((var + pow(mn, 2)) * 2/5) + (mn * 2/3)) / 2;
alexbrandmeyer@621 160 fir << a/2, 1 - a - b, b/2;
alexbrandmeyer@621 161 fir_ok = (fir(2) >= 0.1) ? true : false;
alexbrandmeyer@621 162 break;
alexbrandmeyer@621 163 default:
alexbrandmeyer@621 164 break; // Again, we've arrived at a bad n_taps in the design.
alexbrandmeyer@621 165 // TODO alexbrandmeyer. Add proper Google error handling.
alexbrandmeyer@621 166 }
alexbrandmeyer@621 167 }
alexbrandmeyer@621 168 // Once we have the FIR design for this stage we can assign it to the
alexbrandmeyer@621 169 // appropriate data members.
alexbrandmeyer@621 170 agc_coeffs_.agc_spatial_iterations_(stage) = n_iterations;
alexbrandmeyer@621 171 agc_coeffs_.agc_spatial_n_taps_(stage) = n_taps;
alexbrandmeyer@621 172 // TODO alexbrandmeyer replace using Eigen block method
alexbrandmeyer@621 173 agc_coeffs_.agc_spatial_fir_(0, stage) = fir(0);
alexbrandmeyer@621 174 agc_coeffs_.agc_spatial_fir_(1, stage) = fir(1);
alexbrandmeyer@621 175 agc_coeffs_.agc_spatial_fir_(2, stage) = fir(2);
alexbrandmeyer@621 176 total_dc_gain += pow(agc_coeffs_.agc_stage_gain_, (stage));
alexbrandmeyer@621 177 agc_coeffs_.agc_mix_coeffs_(stage) =
alexbrandmeyer@621 178 (stage == 0) ? 0 : mix_coeff / (tau * (fs /decim));
alexbrandmeyer@621 179 }
alexbrandmeyer@621 180 agc_coeffs_.agc_gain_ = total_dc_gain;
alexbrandmeyer@621 181 agc_coeffs_.detect_scale_ = 1 / total_dc_gain;
alexbrandmeyer@621 182 }
alexbrandmeyer@621 183
alexbrandmeyer@621 184 void Ear::DesignIHC(IHCParams ihc_params, long fs) {
alexbrandmeyer@621 185 if (ihc_params.just_hwr_) {
alexbrandmeyer@621 186 ihc_coeffs_.just_hwr_ = ihc_params.just_hwr_;
alexbrandmeyer@621 187 } else {
alexbrandmeyer@621 188 // This section calculates conductance values using two pre-defined scalars.
alexbrandmeyer@621 189 FloatArray x(1);
alexbrandmeyer@621 190 FPType conduct_at_10, conduct_at_0;
alexbrandmeyer@621 191 x << 10;
alexbrandmeyer@621 192 x = CARFACDetect(x);
alexbrandmeyer@621 193 conduct_at_10 = x(0);
alexbrandmeyer@621 194 x << 0;
alexbrandmeyer@621 195 x = CARFACDetect(x);
alexbrandmeyer@621 196 conduct_at_0 = x(0);
alexbrandmeyer@621 197 if (ihc_params.one_cap_) {
alexbrandmeyer@621 198 FPType ro = 1 / conduct_at_10 ;
alexbrandmeyer@621 199 FPType c = ihc_params.tau1_out_ / ro;
alexbrandmeyer@621 200 FPType ri = ihc_params.tau1_in_ / c;
alexbrandmeyer@621 201 FPType saturation_output = 1 / ((2 * ro) + ri);
alexbrandmeyer@621 202 FPType r0 = 1 / conduct_at_0;
alexbrandmeyer@621 203 FPType current = 1 / (ri + r0);
alexbrandmeyer@621 204 ihc_coeffs_.cap1_voltage_ = 1 - (current * ri);
alexbrandmeyer@621 205 ihc_coeffs_.just_hwr_ = false;
alexbrandmeyer@621 206 ihc_coeffs_.lpf_coeff_ = 1 - exp( -1 / (ihc_params.tau_lpf_ * fs));
alexbrandmeyer@621 207 ihc_coeffs_.out1_rate_ = ro / (ihc_params.tau1_out_ * fs);
alexbrandmeyer@621 208 ihc_coeffs_.in1_rate_ = 1 / (ihc_params.tau1_in_ * fs);
alexbrandmeyer@621 209 ihc_coeffs_.one_cap_ = ihc_params.one_cap_;
alexbrandmeyer@621 210 ihc_coeffs_.output_gain_ = 1 / (saturation_output - current);
alexbrandmeyer@621 211 ihc_coeffs_.rest_output_ = current / (saturation_output - current);
alexbrandmeyer@621 212 ihc_coeffs_.rest_cap1_ = ihc_coeffs_.cap1_voltage_;
alexbrandmeyer@621 213 } else {
alexbrandmeyer@621 214 FPType ro = 1 / conduct_at_10;
alexbrandmeyer@621 215 FPType c2 = ihc_params.tau2_out_ / ro;
alexbrandmeyer@621 216 FPType r2 = ihc_params.tau2_in_ / c2;
alexbrandmeyer@621 217 FPType c1 = ihc_params.tau1_out_ / r2;
alexbrandmeyer@621 218 FPType r1 = ihc_params.tau1_in_ / c1;
alexbrandmeyer@621 219 FPType saturation_output = 1 / (2 * ro + r2 + r1);
alexbrandmeyer@621 220 FPType r0 = 1 / conduct_at_0;
alexbrandmeyer@621 221 FPType current = 1 / (r1 + r2 + r0);
alexbrandmeyer@621 222 ihc_coeffs_.cap1_voltage_ = 1 - (current * r1);
alexbrandmeyer@621 223 ihc_coeffs_.cap2_voltage_ = ihc_coeffs_.cap1_voltage_ - (current * r2);
alexbrandmeyer@621 224 ihc_coeffs_.just_hwr_ = false;
alexbrandmeyer@621 225 ihc_coeffs_.lpf_coeff_ = 1 - exp(-1 / (ihc_params.tau_lpf_ * fs));
alexbrandmeyer@621 226 ihc_coeffs_.out1_rate_ = 1 / (ihc_params.tau1_out_ * fs);
alexbrandmeyer@621 227 ihc_coeffs_.in1_rate_ = 1 / (ihc_params.tau1_in_ * fs);
alexbrandmeyer@621 228 ihc_coeffs_.out2_rate_ = ro / (ihc_params.tau2_out_ * fs);
alexbrandmeyer@621 229 ihc_coeffs_.in2_rate_ = 1 / (ihc_params.tau2_in_ * fs);
alexbrandmeyer@621 230 ihc_coeffs_.one_cap_ = ihc_params.one_cap_;
alexbrandmeyer@621 231 ihc_coeffs_.output_gain_ = 1 / (saturation_output - current);
alexbrandmeyer@621 232 ihc_coeffs_.rest_output_ = current / (saturation_output - current);
alexbrandmeyer@621 233 ihc_coeffs_.rest_cap1_ = ihc_coeffs_.cap1_voltage_;
alexbrandmeyer@621 234 ihc_coeffs_.rest_cap2_ = ihc_coeffs_.cap2_voltage_;
alexbrandmeyer@621 235 }
alexbrandmeyer@621 236 }
alexbrandmeyer@621 237 ihc_coeffs_.ac_coeff_ = 2 * PI * ihc_params.ac_corner_hz_ / fs;
alexbrandmeyer@621 238 }
alexbrandmeyer@621 239
alexbrandmeyer@621 240 void Ear::InitCARState() {
alexbrandmeyer@621 241 car_state_.z1_memory_ = FloatArray::Zero(n_ch_);
alexbrandmeyer@621 242 car_state_.z2_memory_ = FloatArray::Zero(n_ch_);
alexbrandmeyer@621 243 car_state_.za_memory_ = FloatArray::Zero(n_ch_);
alexbrandmeyer@621 244 car_state_.zb_memory_ = car_coeffs_.zr_coeffs_;
alexbrandmeyer@621 245 car_state_.dzb_memory_ = FloatArray::Zero(n_ch_);
alexbrandmeyer@621 246 car_state_.zy_memory_ = FloatArray::Zero(n_ch_);
alexbrandmeyer@621 247 car_state_.g_memory_ = car_coeffs_.g0_coeffs_;
alexbrandmeyer@621 248 car_state_.dg_memory_ = FloatArray::Zero(n_ch_);
alexbrandmeyer@621 249 }
alexbrandmeyer@621 250
alexbrandmeyer@621 251 void Ear::InitIHCState() {
alexbrandmeyer@621 252 ihc_state_.ihc_accum_ = FloatArray::Zero(n_ch_);
alexbrandmeyer@621 253 if (! ihc_coeffs_.just_hwr_) {
alexbrandmeyer@621 254 ihc_state_.ac_coupler_ = FloatArray::Zero(n_ch_);
alexbrandmeyer@621 255 ihc_state_.lpf1_state_ = ihc_coeffs_.rest_output_ * FloatArray::Ones(n_ch_);
alexbrandmeyer@621 256 ihc_state_.lpf2_state_ = ihc_coeffs_.rest_output_ * FloatArray::Ones(n_ch_);
alexbrandmeyer@621 257 if (ihc_coeffs_.one_cap_) {
alexbrandmeyer@621 258 ihc_state_.cap1_voltage_ = ihc_coeffs_.rest_cap1_ *
alexbrandmeyer@621 259 FloatArray::Ones(n_ch_);
alexbrandmeyer@621 260 } else {
alexbrandmeyer@621 261 ihc_state_.cap1_voltage_ = ihc_coeffs_.rest_cap1_ *
alexbrandmeyer@621 262 FloatArray::Ones(n_ch_);
alexbrandmeyer@621 263 ihc_state_.cap2_voltage_ = ihc_coeffs_.rest_cap2_ *
alexbrandmeyer@621 264 FloatArray::Ones(n_ch_);
alexbrandmeyer@621 265 }
alexbrandmeyer@621 266 }
alexbrandmeyer@621 267 }
alexbrandmeyer@621 268
alexbrandmeyer@621 269 void Ear::InitAGCState() {
alexbrandmeyer@621 270 agc_state_.n_agc_stages_ = agc_coeffs_.n_agc_stages_;
alexbrandmeyer@621 271 agc_state_.agc_memory_.resize(agc_state_.n_agc_stages_);
alexbrandmeyer@621 272 agc_state_.input_accum_.resize(agc_state_.n_agc_stages_);
alexbrandmeyer@621 273 agc_state_.decim_phase_.resize(agc_state_.n_agc_stages_);
alexbrandmeyer@621 274 for (int i = 0; i < agc_state_.n_agc_stages_; i++) {
alexbrandmeyer@621 275 agc_state_.decim_phase_.at(i) = 0;
alexbrandmeyer@621 276 agc_state_.agc_memory_.at(i) = FloatArray::Zero(n_ch_);
alexbrandmeyer@621 277 agc_state_.input_accum_.at(i) = FloatArray::Zero(n_ch_);
alexbrandmeyer@621 278 }
alexbrandmeyer@621 279 }
alexbrandmeyer@621 280
alexbrandmeyer@621 281 FloatArray Ear::CARStep(FPType input) {
alexbrandmeyer@620 282 FloatArray g(n_ch_);
alexbrandmeyer@620 283 FloatArray zb(n_ch_);
alexbrandmeyer@620 284 FloatArray za(n_ch_);
alexbrandmeyer@620 285 FloatArray v(n_ch_);
alexbrandmeyer@620 286 FloatArray nlf(n_ch_);
alexbrandmeyer@620 287 FloatArray r(n_ch_);
alexbrandmeyer@620 288 FloatArray z1(n_ch_);
alexbrandmeyer@620 289 FloatArray z2(n_ch_);
alexbrandmeyer@620 290 FloatArray zy(n_ch_);
alexbrandmeyer@620 291 FPType in_out;
alexbrandmeyer@621 292 // This performs the DOHC stuff.
alexbrandmeyer@621 293 g = car_state_.g_memory_ + car_state_.dg_memory_; // This interpolates g.
alexbrandmeyer@621 294 // This calculates the AGC interpolation state.
alexbrandmeyer@621 295 zb = car_state_.zb_memory_ + car_state_.dzb_memory_;
alexbrandmeyer@621 296 // This updates the nonlinear function of 'velocity' along with zA, which is
alexbrandmeyer@621 297 // a delay of z2.
alexbrandmeyer@620 298 za = car_state_.za_memory_;
alexbrandmeyer@620 299 v = car_state_.z2_memory_ - za;
alexbrandmeyer@620 300 nlf = OHC_NLF(v);
alexbrandmeyer@621 301 // Here, zb * nfl is "undamping" delta r.
alexbrandmeyer@621 302 r = car_coeffs_.r1_coeffs_ + (zb * nlf);
alexbrandmeyer@620 303 za = car_state_.z2_memory_;
alexbrandmeyer@621 304 // Here we reduce the CAR state by r and rotate with the fixed cos/sin coeffs.
alexbrandmeyer@620 305 z1 = r * ((car_coeffs_.a0_coeffs_ * car_state_.z1_memory_) -
alexbrandmeyer@620 306 (car_coeffs_.c0_coeffs_ * car_state_.z2_memory_));
alexbrandmeyer@620 307 z2 = r * ((car_coeffs_.c0_coeffs_ * car_state_.z1_memory_) +
alexbrandmeyer@620 308 (car_coeffs_.a0_coeffs_ * car_state_.z2_memory_));
alexbrandmeyer@620 309 zy = car_coeffs_.h_coeffs_ * z2;
alexbrandmeyer@621 310 // This section ripples the input-output path, to avoid delay...
alexbrandmeyer@621 311 // It's the only part that doesn't get computed "in parallel":
alexbrandmeyer@620 312 in_out = input;
alexbrandmeyer@621 313 for (int ch = 0; ch < n_ch_; ch++) {
alexbrandmeyer@620 314 z1(ch) = z1(ch) + in_out;
alexbrandmeyer@621 315 // This performs the ripple, and saves the final channel outputs in zy.
alexbrandmeyer@620 316 in_out = g(ch) * (in_out + zy(ch));
alexbrandmeyer@620 317 zy(ch) = in_out;
alexbrandmeyer@620 318 }
alexbrandmeyer@620 319 car_state_.z1_memory_ = z1;
alexbrandmeyer@620 320 car_state_.z2_memory_ = z2;
alexbrandmeyer@620 321 car_state_.za_memory_ = za;
alexbrandmeyer@620 322 car_state_.zb_memory_ = zb;
alexbrandmeyer@620 323 car_state_.zy_memory_ = zy;
alexbrandmeyer@620 324 car_state_.g_memory_ = g;
alexbrandmeyer@621 325 // The output of the CAR is equal to the zy state.
alexbrandmeyer@620 326 return zy;
alexbrandmeyer@620 327 }
alexbrandmeyer@620 328
alexbrandmeyer@621 329 // We start with a quadratic nonlinear function, and limit it via a
alexbrandmeyer@621 330 // rational function. This makes the result go to zero at high
alexbrandmeyer@620 331 // absolute velocities, so it will do nothing there.
alexbrandmeyer@621 332 FloatArray Ear::OHC_NLF(FloatArray velocities) {
alexbrandmeyer@621 333 return 1 / (1 +
alexbrandmeyer@621 334 (((velocities * car_coeffs_.velocity_scale_) + car_coeffs_.v_offset_) *
alexbrandmeyer@621 335 ((velocities * car_coeffs_.velocity_scale_) + car_coeffs_.v_offset_)));
alexbrandmeyer@621 336
alexbrandmeyer@620 337 }
alexbrandmeyer@620 338
alexbrandmeyer@621 339 // This step is a one sample-time update of the inner-hair-cell (IHC) model,
alexbrandmeyer@621 340 // including the detection nonlinearity and either one or two capacitor state
alexbrandmeyer@621 341 // variables.
alexbrandmeyer@621 342 FloatArray Ear::IHCStep(FloatArray car_out) {
alexbrandmeyer@620 343 FloatArray ihc_out(n_ch_);
alexbrandmeyer@620 344 FloatArray ac_diff(n_ch_);
alexbrandmeyer@620 345 FloatArray conductance(n_ch_);
alexbrandmeyer@620 346 ac_diff = car_out - ihc_state_.ac_coupler_;
alexbrandmeyer@620 347 ihc_state_.ac_coupler_ = ihc_state_.ac_coupler_ +
alexbrandmeyer@620 348 (ihc_coeffs_.ac_coeff_ * ac_diff);
alexbrandmeyer@620 349 if (ihc_coeffs_.just_hwr_) {
alexbrandmeyer@621 350 for (int ch = 0; ch < n_ch_; ch++) {
alexbrandmeyer@620 351 FPType a;
alexbrandmeyer@621 352 a = (ac_diff(ch) > 0) ? ac_diff(ch) : 0;
alexbrandmeyer@621 353 ihc_out(ch) = (a < 2) ? a : 2;
alexbrandmeyer@620 354 }
alexbrandmeyer@620 355 } else {
alexbrandmeyer@620 356 conductance = CARFACDetect(ac_diff);
alexbrandmeyer@620 357 if (ihc_coeffs_.one_cap_) {
alexbrandmeyer@620 358 ihc_out = conductance * ihc_state_.cap1_voltage_;
alexbrandmeyer@620 359 ihc_state_.cap1_voltage_ = ihc_state_.cap1_voltage_ -
alexbrandmeyer@620 360 (ihc_out * ihc_coeffs_.out1_rate_) +
alexbrandmeyer@620 361 ((1 - ihc_state_.cap1_voltage_)
alexbrandmeyer@620 362 * ihc_coeffs_.in1_rate_);
alexbrandmeyer@620 363 } else {
alexbrandmeyer@620 364 ihc_out = conductance * ihc_state_.cap2_voltage_;
alexbrandmeyer@620 365 ihc_state_.cap1_voltage_ = ihc_state_.cap1_voltage_ -
alexbrandmeyer@620 366 ((ihc_state_.cap1_voltage_ - ihc_state_.cap2_voltage_)
alexbrandmeyer@620 367 * ihc_coeffs_.out1_rate_) +
alexbrandmeyer@620 368 ((1 - ihc_state_.cap1_voltage_) * ihc_coeffs_.in1_rate_);
alexbrandmeyer@620 369 ihc_state_.cap2_voltage_ = ihc_state_.cap2_voltage_ -
alexbrandmeyer@620 370 (ihc_out * ihc_coeffs_.out2_rate_) +
alexbrandmeyer@620 371 ((ihc_state_.cap1_voltage_ - ihc_state_.cap2_voltage_)
alexbrandmeyer@620 372 * ihc_coeffs_.in2_rate_);
alexbrandmeyer@620 373 }
alexbrandmeyer@621 374 // Here we smooth the output twice using a LPF.
alexbrandmeyer@621 375 ihc_out *= ihc_coeffs_.output_gain_;
alexbrandmeyer@620 376 ihc_state_.lpf1_state_ = ihc_state_.lpf1_state_ +
alexbrandmeyer@620 377 (ihc_coeffs_.lpf_coeff_ * (ihc_out - ihc_state_.lpf1_state_));
alexbrandmeyer@620 378 ihc_state_.lpf2_state_ = ihc_state_.lpf2_state_ +
alexbrandmeyer@620 379 (ihc_coeffs_.lpf_coeff_ *
alexbrandmeyer@620 380 (ihc_state_.lpf1_state_ - ihc_state_.lpf2_state_));
alexbrandmeyer@620 381 ihc_out = ihc_state_.lpf2_state_ - ihc_coeffs_.rest_output_;
alexbrandmeyer@620 382 }
alexbrandmeyer@620 383 ihc_state_.ihc_accum_ += ihc_out;
alexbrandmeyer@620 384 return ihc_out;
alexbrandmeyer@620 385 }
alexbrandmeyer@620 386
alexbrandmeyer@621 387 bool Ear::AGCStep(FloatArray ihc_out) {
alexbrandmeyer@620 388 int stage = 0;
alexbrandmeyer@620 389 FloatArray agc_in(n_ch_);
alexbrandmeyer@620 390 agc_in = agc_coeffs_.detect_scale_ * ihc_out;
alexbrandmeyer@620 391 bool updated = AGCRecurse(stage, agc_in);
alexbrandmeyer@620 392 return updated;
alexbrandmeyer@620 393 }
alexbrandmeyer@620 394
alexbrandmeyer@621 395 bool Ear::AGCRecurse(int stage, FloatArray agc_in) {
alexbrandmeyer@620 396 bool updated = true;
alexbrandmeyer@621 397 // This is the decim factor for this stage, relative to input or prev. stage:
alexbrandmeyer@621 398 int decim = agc_coeffs_.decimation_.at(stage);
alexbrandmeyer@621 399 // This is the decim phase of this stage (do work on phase 0 only):
alexbrandmeyer@621 400 int decim_phase = agc_state_.decim_phase_.at(stage);
alexbrandmeyer@620 401 decim_phase = decim_phase % decim;
alexbrandmeyer@621 402 agc_state_.decim_phase_.at(stage) = decim_phase;
alexbrandmeyer@621 403 // Here we accumulate input for this stage from the previous stage:
alexbrandmeyer@621 404 agc_state_.input_accum_.at(stage) += agc_in;
alexbrandmeyer@621 405 // We don't do anything if it's not the right decim_phase.
alexbrandmeyer@621 406 if (decim_phase == 0) {
alexbrandmeyer@621 407 // Now we do lots of work, at the decimated rate.
alexbrandmeyer@621 408 // These are the decimated inputs for this stage, which will be further
alexbrandmeyer@621 409 // decimated at the next stage.
alexbrandmeyer@621 410 agc_in = agc_state_.input_accum_.at(stage) / decim;
alexbrandmeyer@621 411 // This resets the accumulator.
alexbrandmeyer@621 412 agc_state_.input_accum_.at(stage) = FloatArray::Zero(n_ch_);
alexbrandmeyer@621 413 if (stage < (agc_coeffs_.decimation_.size() - 1)) {
alexbrandmeyer@621 414 // Now we recurse to evaluate the next stage(s).
alexbrandmeyer@621 415 updated = AGCRecurse(stage + 1, agc_in);
alexbrandmeyer@621 416 // Afterwards we add its output to this stage input, whether it updated or
alexbrandmeyer@621 417 // not.
alexbrandmeyer@621 418 agc_in += (agc_coeffs_.agc_stage_gain_ *
alexbrandmeyer@621 419 agc_state_.agc_memory_.at(stage + 1));
alexbrandmeyer@620 420 }
alexbrandmeyer@621 421 FloatArray agc_stage_state = agc_state_.agc_memory_.at(stage);
alexbrandmeyer@621 422 // This performs a first-order recursive smoothing filter update, in time.
alexbrandmeyer@620 423 agc_stage_state = agc_stage_state + (agc_coeffs_.agc_epsilon_(stage) *
alexbrandmeyer@620 424 (agc_in - agc_stage_state));
alexbrandmeyer@620 425 agc_stage_state = AGCSpatialSmooth(stage, agc_stage_state);
alexbrandmeyer@621 426 agc_state_.agc_memory_.at(stage) = agc_stage_state;
alexbrandmeyer@620 427 } else {
alexbrandmeyer@620 428 updated = false;
alexbrandmeyer@620 429 }
alexbrandmeyer@620 430 return updated;
alexbrandmeyer@620 431 }
alexbrandmeyer@620 432
alexbrandmeyer@621 433 FloatArray Ear::AGCSpatialSmooth(int stage, FloatArray stage_state) {
alexbrandmeyer@620 434 int n_iterations = agc_coeffs_.agc_spatial_iterations_(stage);
alexbrandmeyer@620 435 bool use_fir;
alexbrandmeyer@621 436 use_fir = (n_iterations < 4) ? true : false;
alexbrandmeyer@620 437 if (use_fir) {
alexbrandmeyer@621 438 FloatArray fir_coeffs = agc_coeffs_.agc_spatial_fir_.block(0, stage, 3, 1);
alexbrandmeyer@620 439 FloatArray ss_tap1(n_ch_);
alexbrandmeyer@620 440 FloatArray ss_tap2(n_ch_);
alexbrandmeyer@620 441 FloatArray ss_tap3(n_ch_);
alexbrandmeyer@620 442 FloatArray ss_tap4(n_ch_);
alexbrandmeyer@620 443 int n_taps = agc_coeffs_.agc_spatial_n_taps_(stage);
alexbrandmeyer@621 444 // This initializes the first two taps of stage state, which are used for
alexbrandmeyer@621 445 // both possible cases.
alexbrandmeyer@620 446 ss_tap1(0) = stage_state(0);
alexbrandmeyer@621 447 ss_tap1.block(1, 0, n_ch_ - 1, 1) = stage_state.block(0, 0, n_ch_ - 1, 1);
alexbrandmeyer@621 448 ss_tap2(n_ch_ - 1) = stage_state(n_ch_ - 1);
alexbrandmeyer@621 449 ss_tap2.block(0, 0, n_ch_ - 1, 1) = stage_state.block(1, 0, n_ch_ - 1, 1);
alexbrandmeyer@620 450 switch (n_taps) {
alexbrandmeyer@620 451 case 3:
alexbrandmeyer@620 452 stage_state = (fir_coeffs(0) * ss_tap1) +
alexbrandmeyer@620 453 (fir_coeffs(1) * stage_state) +
alexbrandmeyer@620 454 (fir_coeffs(2) * ss_tap2);
alexbrandmeyer@620 455 break;
alexbrandmeyer@620 456 case 5:
alexbrandmeyer@621 457 // Now we initialize last two taps of stage state, which are only used
alexbrandmeyer@621 458 // for the 5-tap case.
alexbrandmeyer@620 459 ss_tap3(0) = stage_state(0);
alexbrandmeyer@620 460 ss_tap3(1) = stage_state(1);
alexbrandmeyer@621 461 ss_tap3.block(2, 0, n_ch_ - 2, 1) =
alexbrandmeyer@621 462 stage_state.block(0, 0, n_ch_ - 2, 1);
alexbrandmeyer@621 463 ss_tap4(n_ch_ - 2) = stage_state(n_ch_ - 1);
alexbrandmeyer@621 464 ss_tap4(n_ch_ - 1) = stage_state(n_ch_ - 2);
alexbrandmeyer@621 465 ss_tap4.block(0, 0, n_ch_ - 2, 1) =
alexbrandmeyer@621 466 stage_state.block(2, 0, n_ch_ - 2, 1);
alexbrandmeyer@620 467 stage_state = (fir_coeffs(0) * (ss_tap3 + ss_tap1)) +
alexbrandmeyer@620 468 (fir_coeffs(1) * stage_state) +
alexbrandmeyer@620 469 (fir_coeffs(2) * (ss_tap2 + ss_tap4));
alexbrandmeyer@620 470 break;
alexbrandmeyer@620 471 default:
alexbrandmeyer@621 472 // TODO alexbrandmeyer: determine proper error handling implementation.
alexbrandmeyer@620 473 std::cout << "Error: bad n-taps in AGCSpatialSmooth" << std::endl;
alexbrandmeyer@620 474 }
alexbrandmeyer@620 475 } else {
alexbrandmeyer@621 476 stage_state = AGCSmoothDoubleExponential(stage_state,
alexbrandmeyer@621 477 agc_coeffs_.agc_pole_z1_(stage),
alexbrandmeyer@621 478 agc_coeffs_.agc_pole_z2_(stage));
alexbrandmeyer@620 479 }
alexbrandmeyer@620 480 return stage_state;
alexbrandmeyer@620 481 }
alexbrandmeyer@620 482
alexbrandmeyer@621 483 FloatArray Ear::AGCSmoothDoubleExponential(FloatArray stage_state,
alexbrandmeyer@621 484 FPType pole_z1, FPType pole_z2) {
alexbrandmeyer@621 485 int n_pts = stage_state.size();
alexbrandmeyer@621 486 FPType input;
alexbrandmeyer@621 487 FPType state = 0;
alexbrandmeyer@621 488 // TODO alexbrandmeyer: I'm assuming one dimensional input for now, but this
alexbrandmeyer@621 489 // should be verified with Dick for the final version
alexbrandmeyer@621 490 for (int i = n_pts - 11; i < n_pts; i ++){
alexbrandmeyer@621 491 input = stage_state(i);
alexbrandmeyer@621 492 state = state + (1 - pole_z1) * (input - state);
alexbrandmeyer@621 493 }
alexbrandmeyer@621 494 for (int i = n_pts - 1; i > -1; i --){
alexbrandmeyer@621 495 input = stage_state(i);
alexbrandmeyer@621 496 state = state + (1 - pole_z2) * (input - state);
alexbrandmeyer@621 497 }
alexbrandmeyer@621 498 for (int i = 0; i < n_pts; i ++){
alexbrandmeyer@621 499 input = stage_state(i);
alexbrandmeyer@621 500 state = state + (1 - pole_z1) * (input - state);
alexbrandmeyer@621 501 stage_state(i) = state;
alexbrandmeyer@621 502 }
alexbrandmeyer@620 503 return stage_state;
alexbrandmeyer@620 504 }
alexbrandmeyer@621 505
alexbrandmeyer@621 506 FloatArray Ear::ReturnZAMemory() {
alexbrandmeyer@621 507 return car_state_.za_memory_;
alexbrandmeyer@621 508 }
alexbrandmeyer@621 509
alexbrandmeyer@621 510 FloatArray Ear::ReturnZBMemory() {
alexbrandmeyer@621 511 return car_state_.zb_memory_;
alexbrandmeyer@621 512 }
alexbrandmeyer@621 513
alexbrandmeyer@621 514 FloatArray Ear::ReturnGMemory() {
alexbrandmeyer@621 515 return car_state_.g_memory_;
alexbrandmeyer@621 516 };
alexbrandmeyer@621 517
alexbrandmeyer@621 518 FloatArray Ear::ReturnZRCoeffs() {
alexbrandmeyer@621 519 return car_coeffs_.zr_coeffs_;
alexbrandmeyer@621 520 };
alexbrandmeyer@621 521
alexbrandmeyer@621 522 int Ear::ReturnAGCNStages() {
alexbrandmeyer@621 523 return agc_coeffs_.n_agc_stages_;
alexbrandmeyer@621 524 }
alexbrandmeyer@621 525
alexbrandmeyer@621 526 int Ear::ReturnAGCStateDecimPhase(int stage) {
alexbrandmeyer@621 527 return agc_state_.decim_phase_.at(stage);
alexbrandmeyer@621 528 }
alexbrandmeyer@621 529
alexbrandmeyer@621 530 FPType Ear::ReturnAGCMixCoeff(int stage) {
alexbrandmeyer@621 531 return agc_coeffs_.agc_mix_coeffs_(stage);
alexbrandmeyer@621 532 }
alexbrandmeyer@621 533
alexbrandmeyer@621 534 FloatArray Ear::ReturnAGCStateMemory(int stage) {
alexbrandmeyer@621 535 return agc_state_.agc_memory_.at(stage);
alexbrandmeyer@621 536 }
alexbrandmeyer@621 537
alexbrandmeyer@621 538 int Ear::ReturnAGCDecimation(int stage) {
alexbrandmeyer@621 539 return agc_coeffs_.decimation_.at(stage);
alexbrandmeyer@621 540 }
alexbrandmeyer@621 541
alexbrandmeyer@621 542 void Ear::SetAGCStateMemory(int stage, FloatArray new_values) {
alexbrandmeyer@621 543 agc_state_.agc_memory_.at(stage) = new_values;
alexbrandmeyer@621 544 }
alexbrandmeyer@621 545
alexbrandmeyer@621 546 void Ear::SetCARStateDZBMemory(FloatArray new_values) {
alexbrandmeyer@621 547 car_state_.dzb_memory_ = new_values;
alexbrandmeyer@621 548 }
alexbrandmeyer@621 549
alexbrandmeyer@621 550 void Ear::SetCARStateDGMemory(FloatArray new_values) {
alexbrandmeyer@621 551 car_state_.dg_memory_ = new_values;
alexbrandmeyer@621 552 }
alexbrandmeyer@621 553
alexbrandmeyer@621 554 FloatArray Ear::StageGValue(FloatArray undamping) {
alexbrandmeyer@621 555 FloatArray r1 = car_coeffs_.r1_coeffs_;
alexbrandmeyer@621 556 FloatArray a0 = car_coeffs_.a0_coeffs_;
alexbrandmeyer@621 557 FloatArray c0 = car_coeffs_.c0_coeffs_;
alexbrandmeyer@621 558 FloatArray h = car_coeffs_.h_coeffs_;
alexbrandmeyer@621 559 FloatArray zr = car_coeffs_.zr_coeffs_;
alexbrandmeyer@621 560 FloatArray r = r1 + zr * undamping;
alexbrandmeyer@621 561 return (1 - 2 * r * a0 + (r * r)) / (1 - 2 * r * a0 + h * r * c0 + (r * r));
alexbrandmeyer@621 562 }