Mercurial > hg > aimc
diff trunk/carfac/ear.cc @ 668:933cf18d9a59
Fourth revision of Alex Brandmeyer's C++ implementation. Fixed more style issues, changed AGC structures to vectors, replaced FloatArray2d with vector<FloatArray>, implemented first tests using GTest to verify coefficients and monaural output against Matlab values (stored in aimc/carfac/test_data/). To run tests, change the path stored in carfac_test.h in TEST_SRC_DIR. Added CARFAC_GenerateTestData to the Matlab branch, fixed stage indexing in CARFAC_Cross_Couple.m to reflect changes in AGCCoeffs and AGCState structs.
author | alexbrandmeyer |
---|---|
date | Wed, 22 May 2013 21:30:02 +0000 |
parents | 6ec6b50f13da |
children | 443b522fb593 |
line wrap: on
line diff
--- a/trunk/carfac/ear.cc Tue May 21 21:48:34 2013 +0000 +++ b/trunk/carfac/ear.cc Wed May 22 21:30:02 2013 +0000 @@ -24,18 +24,30 @@ // The 'InitEar' function takes a set of model parameters and initializes the // design coefficients and model state variables needed for running the model -// on a single audio channel. -void Ear::InitEar(int n_ch, int32_t fs, FloatArray pole_freqs, - CARParams car_p, IHCParams ihc_p, AGCParams agc_p) { +// on a single audio channel. +void Ear::InitEar(const int n_ch, const FPType fs, + const FloatArray& pole_freqs, const CARParams& car_params, + const IHCParams& ihc_params, const AGCParams& agc_params) { // The first section of code determines the number of channels that will be // used in the model on the basis of the sample rate and the CAR parameters // that have been passed to this function. n_ch_ = n_ch; // These functions use the parameters that have been passed to design the - // coefficients for the three stages of the model. - DesignFilters(car_p, fs, pole_freqs); - DesignIHC(ihc_p, fs); - DesignAGC(agc_p, fs); + // coefficients for the first two stages of the model. + car_coeffs_.Design(car_params, fs, pole_freqs); + ihc_coeffs_.Design(ihc_params, fs); + // This code initializes the coefficients for each of the AGC stages. + agc_coeffs_.resize(agc_params.n_stages_); + FPType previous_stage_gain = 0.0; + FPType decim = 1.0; + for (int stage = 0; stage < agc_params.n_stages_; ++stage) { + agc_coeffs_[stage].Design(agc_params, stage, fs, previous_stage_gain, + decim); + // Two variables store the decimation and gain levels for use in the design + // of the subsequent stages. + previous_stage_gain = agc_coeffs_[stage].agc_gain_; + decim = agc_coeffs_[stage].decim_; + } // Once the coefficients have been determined, we can initialize the state // variables that will be used during runtime. InitCARState(); @@ -43,204 +55,6 @@ InitAGCState(); } -void Ear::DesignFilters(CARParams car_params, int32_t fs, - FloatArray pole_freqs) { - car_coeffs_.velocity_scale_ = car_params.velocity_scale_; - car_coeffs_.v_offset_ = car_params.v_offset_; - car_coeffs_.r1_coeffs_.resize(n_ch_); - car_coeffs_.a0_coeffs_.resize(n_ch_); - car_coeffs_.c0_coeffs_.resize(n_ch_); - car_coeffs_.h_coeffs_.resize(n_ch_); - car_coeffs_.g0_coeffs_.resize(n_ch_); - FPType f = car_params.zero_ratio_ * car_params.zero_ratio_ - 1; - FloatArray theta = pole_freqs * ((2 * PI) / fs); - car_coeffs_.c0_coeffs_ = sin(theta); - car_coeffs_.a0_coeffs_ = cos(theta); - FPType ff = car_params.high_f_damping_compression_; - FloatArray x = theta / PI; - car_coeffs_.zr_coeffs_ = PI * (x - (ff * (x * x * x))); - FPType max_zeta = car_params.max_zeta_; - FPType min_zeta = car_params.min_zeta_; - car_coeffs_.r1_coeffs_ = (1 - (car_coeffs_.zr_coeffs_ * max_zeta)); - FPType curfreq; - FloatArray erb_freqs(n_ch_); - for (int ch=0; ch < n_ch_; ch++) { - curfreq = pole_freqs(ch); - erb_freqs(ch) = ERBHz(curfreq, car_params.erb_break_freq_, - car_params.erb_q_); - } - FloatArray min_zetas = min_zeta + (0.25 * ((erb_freqs / pole_freqs) - min_zeta)); - car_coeffs_.zr_coeffs_ *= max_zeta - min_zetas; - car_coeffs_.h_coeffs_ = car_coeffs_.c0_coeffs_ * f; - FloatArray relative_undamping = FloatArray::Ones(n_ch_); - FloatArray r = car_coeffs_.r1_coeffs_ + - (car_coeffs_.zr_coeffs_ * relative_undamping); - car_coeffs_.g0_coeffs_ = (1 - (2 * r * car_coeffs_.a0_coeffs_) + (r * r)) / - (1 - (2 * r * car_coeffs_.a0_coeffs_) + - (car_coeffs_.h_coeffs_ * r * car_coeffs_.c0_coeffs_) + - (r * r)); -} - - -void Ear::DesignAGC(AGCParams agc_params, int32_t fs) { - // These data members could probably be initialized locally within the design - // function. - agc_coeffs_.n_agc_stages_ = agc_params.n_stages_; - agc_coeffs_.agc_stage_gain_ = agc_params.agc_stage_gain_; - FloatArray agc1_scales = agc_params.agc1_scales_; - FloatArray agc2_scales = agc_params.agc2_scales_; - FloatArray time_constants = agc_params.time_constants_; - agc_coeffs_.agc_epsilon_.resize(agc_coeffs_.n_agc_stages_); - agc_coeffs_.agc_pole_z1_.resize(agc_coeffs_.n_agc_stages_); - agc_coeffs_.agc_pole_z2_.resize(agc_coeffs_.n_agc_stages_); - agc_coeffs_.agc_spatial_iterations_.resize(agc_coeffs_.n_agc_stages_); - agc_coeffs_.agc_spatial_n_taps_.resize(agc_coeffs_.n_agc_stages_); - agc_coeffs_.agc_spatial_fir_.resize(3,agc_coeffs_.n_agc_stages_); - agc_coeffs_.agc_mix_coeffs_.resize(agc_coeffs_.n_agc_stages_); - FPType mix_coeff = agc_params.agc_mix_coeff_; - int decim = 1; - agc_coeffs_.decimation_ = agc_params.decimation_; - FPType total_dc_gain = 0.0; - // Here we loop through each of the stages of the AGC. - for (int stage=0; stage < agc_coeffs_.n_agc_stages_; stage++) { - FPType tau = time_constants(stage); - decim *= agc_coeffs_.decimation_.at(stage); - agc_coeffs_.agc_epsilon_(stage) = 1 - exp((-1 * decim) / (tau * fs)); - FPType n_times = tau * (fs / decim); - FPType delay = (agc2_scales(stage) - agc1_scales(stage)) / n_times; - FPType spread_sq = (pow(agc1_scales(stage), 2) + - pow(agc2_scales(stage), 2)) / n_times; - FPType u = 1 + (1 / spread_sq); - FPType p = u - sqrt(pow(u, 2) - 1); - FPType dp = delay * (1 - (2 * p) + (p * p)) / 2; - FPType pole_z1 = p - dp; - FPType pole_z2 = p + dp; - agc_coeffs_.agc_pole_z1_(stage) = pole_z1; - agc_coeffs_.agc_pole_z2_(stage) = pole_z2; - int n_taps = 0; - bool fir_ok = false; - int n_iterations = 1; - // This section initializes the FIR coeffs settings at each stage. - FloatArray fir(3); - while (! fir_ok) { - switch (n_taps) { - case 0: - n_taps = 3; - break; - case 3: - n_taps = 5; - break; - case 5: - n_iterations ++; - if (n_iterations > 16){ - // This implies too many iterations, so we shoud indicate and error. - // TODO alexbrandmeyer, proper Google error handling. - } - break; - default: - // This means a bad n_taps has been provided, so there should again be - // an error. - // TODO alexbrandmeyer, proper Google error handling. - break; - } - // Now we can design the FIR coefficients. - FPType var = spread_sq / n_iterations; - FPType mn = delay / n_iterations; - FPType a, b; - switch (n_taps) { - case 3: - a = (var + pow(mn, 2) - mn) / 2; - b = (var + pow(mn, 2) + mn) / 2; - fir(0) = a; - fir(1) = 1 - a - b; - fir(2) = b; - fir_ok = (fir(2) >= 0.2) ? true : false; - break; - case 5: - a = (((var + pow(mn, 2)) * 2/5) - (mn * 2/3)) / 2; - b = (((var + pow(mn, 2)) * 2/5) + (mn * 2/3)) / 2; - fir(0) = a / 2; - fir(1) = 1 - a - b; - fir(2) = b / 2; - fir_ok = (fir(2) >= 0.1) ? true : false; - break; - default: - break; // Again, we've arrived at a bad n_taps in the design. - // TODO alexbrandmeyer. Add proper Google error handling. - } - } - // Once we have the FIR design for this stage we can assign it to the - // appropriate data members. - agc_coeffs_.agc_spatial_iterations_(stage) = n_iterations; - agc_coeffs_.agc_spatial_n_taps_(stage) = n_taps; - // TODO alexbrandmeyer replace using Eigen block method - agc_coeffs_.agc_spatial_fir_(0, stage) = fir(0); - agc_coeffs_.agc_spatial_fir_(1, stage) = fir(1); - agc_coeffs_.agc_spatial_fir_(2, stage) = fir(2); - total_dc_gain += pow(agc_coeffs_.agc_stage_gain_, (stage)); - agc_coeffs_.agc_mix_coeffs_(stage) = - (stage == 0) ? 0 : mix_coeff / (tau * (fs /decim)); - } - agc_coeffs_.agc_gain_ = total_dc_gain; - agc_coeffs_.detect_scale_ = 1 / total_dc_gain; -} - -void Ear::DesignIHC(IHCParams ihc_params, int32_t fs) { - if (ihc_params.just_hwr_) { - ihc_coeffs_.just_hwr_ = ihc_params.just_hwr_; - } else { - // This section calculates conductance values using two pre-defined scalars. - FloatArray x(1); - FPType conduct_at_10, conduct_at_0; - x(0) = 10.0; - x = CARFACDetect(x); - conduct_at_10 = x(0); - x(0) = 0.0; - x = CARFACDetect(x); - conduct_at_0 = x(0); - if (ihc_params.one_cap_) { - FPType ro = 1 / conduct_at_10 ; - FPType c = ihc_params.tau1_out_ / ro; - FPType ri = ihc_params.tau1_in_ / c; - FPType saturation_output = 1 / ((2 * ro) + ri); - FPType r0 = 1 / conduct_at_0; - FPType current = 1 / (ri + r0); - ihc_coeffs_.cap1_voltage_ = 1 - (current * ri); - ihc_coeffs_.just_hwr_ = false; - ihc_coeffs_.lpf_coeff_ = 1 - exp( -1 / (ihc_params.tau_lpf_ * fs)); - ihc_coeffs_.out1_rate_ = ro / (ihc_params.tau1_out_ * fs); - ihc_coeffs_.in1_rate_ = 1 / (ihc_params.tau1_in_ * fs); - ihc_coeffs_.one_cap_ = ihc_params.one_cap_; - ihc_coeffs_.output_gain_ = 1 / (saturation_output - current); - ihc_coeffs_.rest_output_ = current / (saturation_output - current); - ihc_coeffs_.rest_cap1_ = ihc_coeffs_.cap1_voltage_; - } else { - FPType ro = 1 / conduct_at_10; - FPType c2 = ihc_params.tau2_out_ / ro; - FPType r2 = ihc_params.tau2_in_ / c2; - FPType c1 = ihc_params.tau1_out_ / r2; - FPType r1 = ihc_params.tau1_in_ / c1; - FPType saturation_output = 1 / (2 * ro + r2 + r1); - FPType r0 = 1 / conduct_at_0; - FPType current = 1 / (r1 + r2 + r0); - ihc_coeffs_.cap1_voltage_ = 1 - (current * r1); - ihc_coeffs_.cap2_voltage_ = ihc_coeffs_.cap1_voltage_ - (current * r2); - ihc_coeffs_.just_hwr_ = false; - ihc_coeffs_.lpf_coeff_ = 1 - exp(-1 / (ihc_params.tau_lpf_ * fs)); - ihc_coeffs_.out1_rate_ = 1 / (ihc_params.tau1_out_ * fs); - ihc_coeffs_.in1_rate_ = 1 / (ihc_params.tau1_in_ * fs); - ihc_coeffs_.out2_rate_ = ro / (ihc_params.tau2_out_ * fs); - ihc_coeffs_.in2_rate_ = 1 / (ihc_params.tau2_in_ * fs); - ihc_coeffs_.one_cap_ = ihc_params.one_cap_; - ihc_coeffs_.output_gain_ = 1 / (saturation_output - current); - ihc_coeffs_.rest_output_ = current / (saturation_output - current); - ihc_coeffs_.rest_cap1_ = ihc_coeffs_.cap1_voltage_; - ihc_coeffs_.rest_cap2_ = ihc_coeffs_.cap2_voltage_; - } - } - ihc_coeffs_.ac_coeff_ = 2 * PI * ihc_params.ac_corner_hz_ / fs; -} - void Ear::InitCARState() { car_state_.z1_memory_ = FloatArray::Zero(n_ch_); car_state_.z2_memory_ = FloatArray::Zero(n_ch_); @@ -260,189 +74,179 @@ ihc_state_.lpf2_state_ = ihc_coeffs_.rest_output_ * FloatArray::Ones(n_ch_); if (ihc_coeffs_.one_cap_) { ihc_state_.cap1_voltage_ = ihc_coeffs_.rest_cap1_ * - FloatArray::Ones(n_ch_); + FloatArray::Ones(n_ch_); } else { ihc_state_.cap1_voltage_ = ihc_coeffs_.rest_cap1_ * - FloatArray::Ones(n_ch_); + FloatArray::Ones(n_ch_); ihc_state_.cap2_voltage_ = ihc_coeffs_.rest_cap2_ * - FloatArray::Ones(n_ch_); + FloatArray::Ones(n_ch_); } } } void Ear::InitAGCState() { - agc_state_.n_agc_stages_ = agc_coeffs_.n_agc_stages_; - agc_state_.agc_memory_.resize(agc_state_.n_agc_stages_); - agc_state_.input_accum_.resize(agc_state_.n_agc_stages_); - agc_state_.decim_phase_.resize(agc_state_.n_agc_stages_); - for (int i = 0; i < agc_state_.n_agc_stages_; i++) { - agc_state_.decim_phase_.at(i) = 0; - agc_state_.agc_memory_.at(i) = FloatArray::Zero(n_ch_); - agc_state_.input_accum_.at(i) = FloatArray::Zero(n_ch_); + int n_agc_stages = agc_coeffs_.size(); + agc_state_.resize(n_agc_stages); + for (int i = 0; i < n_agc_stages; ++i) { + agc_state_[i].decim_phase_ = 0; + agc_state_[i].agc_memory_ = FloatArray::Zero(n_ch_); + agc_state_[i].input_accum_ = FloatArray::Zero(n_ch_); } } -FloatArray Ear::CARStep(FPType input) { - FloatArray g(n_ch_); - FloatArray zb(n_ch_); - FloatArray za(n_ch_); - FloatArray v(n_ch_); - FloatArray nlf(n_ch_); - FloatArray r(n_ch_); - FloatArray z1(n_ch_); - FloatArray z2(n_ch_); - FloatArray zy(n_ch_); - FPType in_out; - // This performs the DOHC stuff. - g = car_state_.g_memory_ + car_state_.dg_memory_; // This interpolates g. +void Ear::CARStep(const FPType input, FloatArray* car_out) { + // This interpolates g. + car_state_.g_memory_ = car_state_.g_memory_ + car_state_.dg_memory_; // This calculates the AGC interpolation state. - zb = car_state_.zb_memory_ + car_state_.dzb_memory_; + car_state_.zb_memory_ = car_state_.zb_memory_ + car_state_.dzb_memory_; // This updates the nonlinear function of 'velocity' along with zA, which is // a delay of z2. - za = car_state_.za_memory_; - v = car_state_.z2_memory_ - za; - nlf = OHC_NLF(v); - // Here, zb * nfl is "undamping" delta r. - r = car_coeffs_.r1_coeffs_ + (zb * nlf); - za = car_state_.z2_memory_; + FloatArray nonlinear_fun(n_ch_); + FloatArray velocities = car_state_.z2_memory_ - car_state_.za_memory_; + OHCNonlinearFunction(velocities, &nonlinear_fun); + // Here, zb_memory_ * nonlinear_fun is "undamping" delta r. + FloatArray r = car_coeffs_.r1_coeffs_ + (car_state_.zb_memory_ * + nonlinear_fun); + car_state_.za_memory_ = car_state_.z2_memory_; // Here we reduce the CAR state by r and rotate with the fixed cos/sin coeffs. - z1 = r * ((car_coeffs_.a0_coeffs_ * car_state_.z1_memory_) - - (car_coeffs_.c0_coeffs_ * car_state_.z2_memory_)); - z2 = r * ((car_coeffs_.c0_coeffs_ * car_state_.z1_memory_) + - (car_coeffs_.a0_coeffs_ * car_state_.z2_memory_)); - zy = car_coeffs_.h_coeffs_ * z2; + FloatArray z1 = r * ((car_coeffs_.a0_coeffs_ * car_state_.z1_memory_) - + (car_coeffs_.c0_coeffs_ * car_state_.z2_memory_)); + car_state_.z2_memory_ = r * + ((car_coeffs_.c0_coeffs_ * car_state_.z1_memory_) + + (car_coeffs_.a0_coeffs_ * car_state_.z2_memory_)); + car_state_.zy_memory_ = car_coeffs_.h_coeffs_ * car_state_.z2_memory_; // This section ripples the input-output path, to avoid delay... // It's the only part that doesn't get computed "in parallel": - in_out = input; + FPType in_out = input; for (int ch = 0; ch < n_ch_; ch++) { z1(ch) = z1(ch) + in_out; // This performs the ripple, and saves the final channel outputs in zy. - in_out = g(ch) * (in_out + zy(ch)); - zy(ch) = in_out; + in_out = car_state_.g_memory_(ch) * (in_out + car_state_.zy_memory_(ch)); + car_state_.zy_memory_(ch) = in_out; } car_state_.z1_memory_ = z1; - car_state_.z2_memory_ = z2; - car_state_.za_memory_ = za; - car_state_.zb_memory_ = zb; - car_state_.zy_memory_ = zy; - car_state_.g_memory_ = g; - // The output of the CAR is equal to the zy state. - return zy; + *car_out = car_state_.zy_memory_; } // We start with a quadratic nonlinear function, and limit it via a // rational function. This makes the result go to zero at high // absolute velocities, so it will do nothing there. -FloatArray Ear::OHC_NLF(FloatArray velocities) { - return (1 + ((velocities * car_coeffs_.velocity_scale_) - + car_coeffs_.v_offset_).square()).inverse(); +void Ear::OHCNonlinearFunction(const FloatArray& velocities, + FloatArray* nonlinear_fun) { + *nonlinear_fun = (1 + ((velocities * car_coeffs_.velocity_scale_) + + car_coeffs_.v_offset_).square()).inverse(); } // This step is a one sample-time update of the inner-hair-cell (IHC) model, // including the detection nonlinearity and either one or two capacitor state // variables. -FloatArray Ear::IHCStep(FloatArray car_out) { - FloatArray ihc_out(n_ch_); - FloatArray ac_diff(n_ch_); - FloatArray conductance(n_ch_); - ac_diff = car_out - ihc_state_.ac_coupler_; +void Ear::IHCStep(const FloatArray& car_out, FloatArray* ihc_out) { + FloatArray ac_diff = car_out - ihc_state_.ac_coupler_; ihc_state_.ac_coupler_ = ihc_state_.ac_coupler_ + - (ihc_coeffs_.ac_coeff_ * ac_diff); + (ihc_coeffs_.ac_coeff_ * ac_diff); if (ihc_coeffs_.just_hwr_) { - for (int ch = 0; ch < n_ch_; ch++) { - FPType a; - a = (ac_diff(ch) > 0.0) ? ac_diff(ch) : 0.0; - ihc_out(ch) = (a < 2) ? a : 2; + FloatArray output(n_ch_); + for (int ch = 0; ch < n_ch_; ++ch) { + FPType a = (ac_diff(ch) > 0.0) ? ac_diff(ch) : 0.0; + output(ch) = (a < 2) ? a : 2; } + *ihc_out = output; } else { - conductance = CARFACDetect(ac_diff); + FloatArray conductance = CARFACDetect(ac_diff); if (ihc_coeffs_.one_cap_) { - ihc_out = conductance * ihc_state_.cap1_voltage_; + *ihc_out = conductance * ihc_state_.cap1_voltage_; ihc_state_.cap1_voltage_ = ihc_state_.cap1_voltage_ - - (ihc_out * ihc_coeffs_.out1_rate_) + - ((1 - ihc_state_.cap1_voltage_) - * ihc_coeffs_.in1_rate_); + (*ihc_out * ihc_coeffs_.out1_rate_) + + ((1 - ihc_state_.cap1_voltage_) + * ihc_coeffs_.in1_rate_); } else { - ihc_out = conductance * ihc_state_.cap2_voltage_; + *ihc_out = conductance * ihc_state_.cap2_voltage_; ihc_state_.cap1_voltage_ = ihc_state_.cap1_voltage_ - - ((ihc_state_.cap1_voltage_ - ihc_state_.cap2_voltage_) - * ihc_coeffs_.out1_rate_) + - ((1 - ihc_state_.cap1_voltage_) * ihc_coeffs_.in1_rate_); + ((ihc_state_.cap1_voltage_ - ihc_state_.cap2_voltage_) + * ihc_coeffs_.out1_rate_) + + ((1 - ihc_state_.cap1_voltage_) * ihc_coeffs_.in1_rate_); ihc_state_.cap2_voltage_ = ihc_state_.cap2_voltage_ - - (ihc_out * ihc_coeffs_.out2_rate_) + - ((ihc_state_.cap1_voltage_ - ihc_state_.cap2_voltage_) - * ihc_coeffs_.in2_rate_); + (*ihc_out * ihc_coeffs_.out2_rate_) + + ((ihc_state_.cap1_voltage_ - ihc_state_.cap2_voltage_) + * ihc_coeffs_.in2_rate_); } // Here we smooth the output twice using a LPF. - ihc_out *= ihc_coeffs_.output_gain_; + *ihc_out *= ihc_coeffs_.output_gain_; ihc_state_.lpf1_state_ = ihc_state_.lpf1_state_ + - (ihc_coeffs_.lpf_coeff_ * (ihc_out - ihc_state_.lpf1_state_)); + (ihc_coeffs_.lpf_coeff_ * + (*ihc_out - ihc_state_.lpf1_state_)); ihc_state_.lpf2_state_ = ihc_state_.lpf2_state_ + - (ihc_coeffs_.lpf_coeff_ * - (ihc_state_.lpf1_state_ - ihc_state_.lpf2_state_)); - ihc_out = ihc_state_.lpf2_state_ - ihc_coeffs_.rest_output_; + (ihc_coeffs_.lpf_coeff_ * + (ihc_state_.lpf1_state_ - ihc_state_.lpf2_state_)); + *ihc_out = ihc_state_.lpf2_state_ - ihc_coeffs_.rest_output_; } - ihc_state_.ihc_accum_ += ihc_out; - return ihc_out; + ihc_state_.ihc_accum_ += *ihc_out; } -bool Ear::AGCStep(FloatArray ihc_out) { +bool Ear::AGCStep(const FloatArray& ihc_out) { int stage = 0; - FloatArray agc_in(n_ch_); - agc_in = agc_coeffs_.detect_scale_ * ihc_out; - bool updated = AGCRecurse(stage, agc_in); + int n_stages = agc_coeffs_[0].n_agc_stages_; + FPType detect_scale = agc_coeffs_[n_stages - 1].detect_scale_; + bool updated = AGCRecurse(stage, detect_scale * ihc_out); return updated; } -bool Ear::AGCRecurse(int stage, FloatArray agc_in) { +bool Ear::AGCRecurse(const int stage, FloatArray agc_in) { bool updated = true; // This is the decim factor for this stage, relative to input or prev. stage: - int decim = agc_coeffs_.decimation_.at(stage); + int decim = agc_coeffs_[stage].decimation_; // This is the decim phase of this stage (do work on phase 0 only): - int decim_phase = agc_state_.decim_phase_.at(stage); + int decim_phase = agc_state_[stage].decim_phase_ + 1; decim_phase = decim_phase % decim; - agc_state_.decim_phase_.at(stage) = decim_phase; + agc_state_[stage].decim_phase_ = decim_phase; // Here we accumulate input for this stage from the previous stage: - agc_state_.input_accum_.at(stage) += agc_in; + agc_state_[stage].input_accum_ += agc_in; // We don't do anything if it's not the right decim_phase. if (decim_phase == 0) { // Now we do lots of work, at the decimated rate. // These are the decimated inputs for this stage, which will be further // decimated at the next stage. - agc_in = agc_state_.input_accum_.at(stage) / decim; + agc_in = agc_state_[stage].input_accum_ / decim; // This resets the accumulator. - agc_state_.input_accum_.at(stage) = FloatArray::Zero(n_ch_); - if (stage < (agc_coeffs_.decimation_.size() - 1)) { + agc_state_[stage].input_accum_ = FloatArray::Zero(n_ch_); + if (stage < (agc_coeffs_.size() - 1)) { // Now we recurse to evaluate the next stage(s). + // TODO (alexbrandmeyer): the Matlab version of AGCRecurse can return a + // value for updated, but isn't used in that version. Check with Dick to + // see if that is needed. updated = AGCRecurse(stage + 1, agc_in); // Afterwards we add its output to this stage input, whether it updated or // not. - agc_in += (agc_coeffs_.agc_stage_gain_ * - agc_state_.agc_memory_.at(stage + 1)); + agc_in += agc_coeffs_[stage].agc_stage_gain_ * + agc_state_[stage + 1].agc_memory_; } - FloatArray agc_stage_state = agc_state_.agc_memory_.at(stage); + FloatArray agc_stage_state = agc_state_[stage].agc_memory_; // This performs a first-order recursive smoothing filter update, in time. - agc_stage_state = agc_stage_state + (agc_coeffs_.agc_epsilon_(stage) * - (agc_in - agc_stage_state)); + agc_stage_state += agc_coeffs_[stage].agc_epsilon_ * + (agc_in - agc_stage_state); agc_stage_state = AGCSpatialSmooth(stage, agc_stage_state); - agc_state_.agc_memory_.at(stage) = agc_stage_state; + agc_state_[stage].agc_memory_ = agc_stage_state; + updated = true; } else { updated = false; } return updated; } -FloatArray Ear::AGCSpatialSmooth(int stage, FloatArray stage_state) { - int n_iterations = agc_coeffs_.agc_spatial_iterations_(stage); +// TODO (alexbrandmeyer): figure out how to operate directly on stage_state. +// Using a pointer breaks the () indexing of the Eigen arrays, but there must +// be a way around this. +FloatArray Ear::AGCSpatialSmooth(const int stage, FloatArray stage_state) { + int n_iterations = agc_coeffs_[stage].agc_spatial_iterations_; bool use_fir; use_fir = (n_iterations < 4) ? true : false; if (use_fir) { - FloatArray fir_coeffs = agc_coeffs_.agc_spatial_fir_.block(0, stage, 3, 1); + std::vector<FPType> fir_coeffs = agc_coeffs_[stage].agc_spatial_fir_; FloatArray ss_tap1(n_ch_); FloatArray ss_tap2(n_ch_); FloatArray ss_tap3(n_ch_); FloatArray ss_tap4(n_ch_); - int n_taps = agc_coeffs_.agc_spatial_n_taps_(stage); + int n_taps = agc_coeffs_[stage].agc_spatial_n_taps_; // This initializes the first two taps of stage state, which are used for // both possible cases. ss_tap1(0) = stage_state(0); @@ -451,9 +255,9 @@ ss_tap2.block(0, 0, n_ch_ - 1, 1) = stage_state.block(1, 0, n_ch_ - 1, 1); switch (n_taps) { case 3: - stage_state = (fir_coeffs(0) * ss_tap1) + - (fir_coeffs(1) * stage_state) + - (fir_coeffs(2) * ss_tap2); + stage_state = (fir_coeffs[0] * ss_tap1) + + (fir_coeffs[1] * stage_state) + + (fir_coeffs[2] * ss_tap2); break; case 5: // Now we initialize last two taps of stage state, which are only used @@ -461,43 +265,47 @@ ss_tap3(0) = stage_state(0); ss_tap3(1) = stage_state(1); ss_tap3.block(2, 0, n_ch_ - 2, 1) = - stage_state.block(0, 0, n_ch_ - 2, 1); + stage_state.block(0, 0, n_ch_ - 2, 1); ss_tap4(n_ch_ - 2) = stage_state(n_ch_ - 1); ss_tap4(n_ch_ - 1) = stage_state(n_ch_ - 2); ss_tap4.block(0, 0, n_ch_ - 2, 1) = - stage_state.block(2, 0, n_ch_ - 2, 1); - stage_state = (fir_coeffs(0) * (ss_tap3 + ss_tap1)) + - (fir_coeffs(1) * stage_state) + - (fir_coeffs(2) * (ss_tap2 + ss_tap4)); + stage_state.block(2, 0, n_ch_ - 2, 1); + stage_state = (fir_coeffs[0] * (ss_tap3 + ss_tap1)) + + (fir_coeffs[1] * stage_state) + + (fir_coeffs[2] * (ss_tap2 + ss_tap4)); break; default: break; - // TODO alexbrandmeyer: determine proper error handling implementation. + CHECK_EQ(5, n_taps) << + "Bad n_taps in AGCSpatialSmooth; should be 3 or 5."; } } else { stage_state = AGCSmoothDoubleExponential(stage_state, - agc_coeffs_.agc_pole_z1_(stage), - agc_coeffs_.agc_pole_z2_(stage)); + agc_coeffs_[stage].agc_pole_z1_, + agc_coeffs_[stage].agc_pole_z2_); } return stage_state; } +// TODO (alexbrandmeyer): figure out how to operate directly on stage_state. +// Same point as above for AGCSpatialSmooth. FloatArray Ear::AGCSmoothDoubleExponential(FloatArray stage_state, - FPType pole_z1, FPType pole_z2) { + const FPType pole_z1, + const FPType pole_z2) { int32_t n_pts = stage_state.size(); FPType input; FPType state = 0.0; - // TODO alexbrandmeyer: I'm assuming one dimensional input for now, but this + // TODO (alexbrandmeyer): I'm assuming one dimensional input for now, but this // should be verified with Dick for the final version - for (int i = n_pts - 11; i < n_pts; i ++){ + for (int i = n_pts - 11; i < n_pts; ++i){ input = stage_state(i); state = state + (1 - pole_z1) * (input - state); } - for (int i = n_pts - 1; i > -1; i --){ + for (int i = n_pts - 1; i > -1; --i){ input = stage_state(i); state = state + (1 - pole_z2) * (input - state); } - for (int i = 0; i < n_pts; i ++){ + for (int i = 0; i < n_pts; ++i){ input = stage_state(i); state = state + (1 - pole_z1) * (input - state); stage_state(i) = state; @@ -505,60 +313,9 @@ return stage_state; } -FloatArray Ear::ReturnZAMemory() { - return car_state_.za_memory_; -} - -FloatArray Ear::ReturnZBMemory() { - return car_state_.zb_memory_; -} - -FloatArray Ear::ReturnGMemory() { - return car_state_.g_memory_; -}; - -FloatArray Ear::ReturnZRCoeffs() { - return car_coeffs_.zr_coeffs_; -}; - -int Ear::ReturnAGCNStages() { - return agc_coeffs_.n_agc_stages_; -} - -int Ear::ReturnAGCStateDecimPhase(int stage) { - return agc_state_.decim_phase_.at(stage); -} - -FPType Ear::ReturnAGCMixCoeff(int stage) { - return agc_coeffs_.agc_mix_coeffs_(stage); -} - -FloatArray Ear::ReturnAGCStateMemory(int stage) { - return agc_state_.agc_memory_.at(stage); -} - -int Ear::ReturnAGCDecimation(int stage) { - return agc_coeffs_.decimation_.at(stage); -} - -void Ear::SetAGCStateMemory(int stage, FloatArray new_values) { - agc_state_.agc_memory_.at(stage) = new_values; -} - -void Ear::SetCARStateDZBMemory(FloatArray new_values) { - car_state_.dzb_memory_ = new_values; -} - -void Ear::SetCARStateDGMemory(FloatArray new_values) { - car_state_.dg_memory_ = new_values; -} - -FloatArray Ear::StageGValue(FloatArray undamping) { - FloatArray r1 = car_coeffs_.r1_coeffs_; - FloatArray a0 = car_coeffs_.a0_coeffs_; - FloatArray c0 = car_coeffs_.c0_coeffs_; - FloatArray h = car_coeffs_.h_coeffs_; - FloatArray zr = car_coeffs_.zr_coeffs_; - FloatArray r = r1 + zr * undamping; - return (1 - 2 * r * a0 + (r * r)) / (1 - 2 * r * a0 + h * r * c0 + (r * r)); -} +FloatArray Ear::StageGValue(const FloatArray& undamping) { + FloatArray r = car_coeffs_.r1_coeffs_ + car_coeffs_.zr_coeffs_ * undamping; + return (1 - 2 * r * car_coeffs_.a0_coeffs_ + (r * r)) / + (1 - 2 * r * car_coeffs_.a0_coeffs_ + + car_coeffs_.h_coeffs_ * r * car_coeffs_.c0_coeffs_ + (r * r)); +} \ No newline at end of file